Merge branch 'extreme_corset' into 'master'

Improve graphics for extreme corset

See merge request 
This commit is contained in:
Da'Inihlus 2025-04-09 14:52:08 +00:00
commit 8463daae4b
16 changed files with 58 additions and 46 deletions

View file

@ -8058,9 +8058,10 @@ var AssetFemale3DCG = [
Hogtied: PoseType.HIDE,
AllFours: PoseType.HIDE,
},
DefaultColor: ["#545454", "#BC8B84"],
DefaultColor: ["#545454", "#EEEEEE", "#BC8B84"],
Layer: [
{ Name: "Main" },
{ Name: "Highlights" },
{ Name: "Buckles" },
{
Name: "BodyMask",
@ -28729,9 +28730,10 @@ var AssetFemale3DCG = [
Hogtied: PoseType.HIDE,
AllFours: PoseType.HIDE,
},
DefaultColor: ["#545454", "#BC8B84"],
DefaultColor: ["#545454", "#EEEEEE", "#BC8B84"],
Layer: [
{ Name: "Main" },
{ Name: "Highlights" },
{ Name: "Buckles" },
{
Name: "BodyMask",
@ -30143,9 +30145,10 @@ var AssetFemale3DCG = [
Hogtied: PoseType.HIDE,
AllFours: PoseType.HIDE,
},
DefaultColor: ["#545454", "#BC8B84"],
DefaultColor: ["#545454", "#EEEEEE", "#BC8B84"],
Layer: [
{ Name: "Main" },
{ Name: "Highlights" },
{ Name: "Buckles" },
{
Name: "BodyMask",

View file

@ -809,6 +809,8 @@ type AssetDefinition = (
interface AssetLayerMaskTexureDefinition {
/** The groups that will be affected */
Groups: AssetGroupName[];
/** If true, the texture mask will apply to all layers in the groups specified, event if their priority is higher than the mask layer */
ApplyToAbove?: boolean;
}
interface AssetLayerDefinition extends AssetCommonPropertiesGroupAssetLayer, AssetCommonPropertiesAssetLayer {

Binary file not shown.

Before

(image error) Size: 2.9 KiB

After

(image error) Size: 4.2 KiB

Binary file not shown.

After

(image error) Size: 15 KiB

Binary file not shown.

Before

(image error) Size: 3 KiB

After

(image error) Size: 4.1 KiB

Binary file not shown.

After

(image error) Size: 15 KiB

Binary file not shown.

Before

(image error) Size: 3.1 KiB

After

(image error) Size: 3.9 KiB

Binary file not shown.

After

(image error) Size: 14 KiB

Binary file not shown.

Before

(image error) Size: 2.9 KiB

After

(image error) Size: 4 KiB

Binary file not shown.

After

(image error) Size: 16 KiB

View file

@ -2368,6 +2368,7 @@ ItemTorsoThinLeatherStrapsStrap,Straps
ItemTorsoShinyLeotardLockLatex,Base
ItemTorsoShinyLeotardLockShine,Shine
ItemTorsoExtremeCorsetMain,Main
ItemTorsoExtremeCorsetHighlights,Highlights
ItemTorsoExtremeCorsetBuckles,Buckles
ItemVulvaBasicCockringCockRing,Cock Ring
ItemVulvaBasicCockringPenis,Penis

1 BodyUpperLarge Body
2368 ItemTorsoShinyLeotardLockLatex Base
2369 ItemTorsoShinyLeotardLockShine Shine
2370 ItemTorsoExtremeCorsetMain Main
2371 ItemTorsoExtremeCorsetHighlights Highlights
2372 ItemTorsoExtremeCorsetBuckles Buckles
2373 ItemVulvaBasicCockringCockRing Cock Ring
2374 ItemVulvaBasicCockringPenis Penis

View file

@ -483,7 +483,7 @@ function CharacterAppearanceSortLayers(C) {
/**
* Builds a map of all mask layers in a character's appearance, grouped by the asset groups they affect.
* Only includes mask layers from visible assets that aren't blocked and are allowed in the current chat room.
*
*
* @param {Character} C - The character whose masks should be built
* @returns {AssetLayer[]} A map of group names to arrays of mask layers that affect them
*/

View file

@ -69,8 +69,8 @@ function CommonDrawAppearancePrepareMaskLayers(C) {
let blendingMode = "destination-in";
if(layer.BlendingMode === "destination-in" || layer.BlendingMode === "destination-out") {
blendingMode = layer.BlendingMode;
}
else if(!!layer.BlendingMode) return acc;
}
else if(layer.BlendingMode) return acc;
// Safeguard against a null pose
if (typeof pose !== "string") pose = /** @type {AssetPoseName} */("");
@ -95,11 +95,14 @@ function CommonDrawAppearancePrepareMaskLayers(C) {
const urlParts = [asset.Name, parentAssetName, layerType, layerSegment].filter(c => c);
const layerURL = urlParts.join("_") + ".png";
const maskPriority = layer.TextureMask.ApplyToAbove ? -1 : (item.Property?.OverridePriority?.[layer.Name] ?? layer.Priority);
/** @type {TextureAlphaMask} */
const maskLayer = {
Url: baseURL + layerURL,
X, Y,
Mode: blendingMode,
Priority: maskPriority,
};
/** @type {TextureAlphaMask} */
@ -107,6 +110,7 @@ function CommonDrawAppearancePrepareMaskLayers(C) {
Url: baseURLBlink + layerURL,
X, Y,
Mode: blendingMode,
Priority: maskPriority,
};
for(const maskedGroup of layer.TextureMask.Groups) {
@ -341,8 +345,8 @@ function CommonDrawAppearanceBuild(C, {
// prepare mask layers for the current group
const maskLayer = maskTexLayers.get(groupName) ?? [];
const maskLayerBlink = maskTexLayersBlink.get(groupName) ?? [];
const maskLayer = (maskTexLayers.get(group.Name) ?? []).filter(m => m.Priority < 0 || m.Priority >= layer.Priority);
const maskLayerBlink = (maskTexLayersBlink.get(group.Name) ?? []).filter(m => m.Priority < 0 || m.Priority >= layer.Priority);
if (shouldColorize) {
drawImageColorize(

View file

@ -585,10 +585,10 @@ function DrawImage(Source, X, Y, Invert) {
/**
* Applies texture masks to a canvas about to be drawn
* @param {CanvasRenderingContext2D} destCanvas
* @param {CanvasRenderingContext2D} destCanvas
* @param {number} X - Position of the image on the X axis
* @param {number} Y - Position of the image on the Y axis
* @param {readonly TextureAlphaMask[]} TextureAlphaMasks
* @param {readonly TextureAlphaMask[]} TextureAlphaMasks
*/
function DrawApplyTextureAlphaMask(destCanvas, X, Y, TextureAlphaMasks) {
const key = "TexMask:" + JSON.stringify({

View file

@ -363,18 +363,18 @@ var GLDrawFragmentShaderSourceHalfAlpha = `
uniform vec4 u_color;
void main() {
vec4 texColor = texture2D(u_texture, v_texcoord);
vec4 alphaColor = texture2D(u_alpha_texture, v_texcoord);
vec4 texColor = texture2D(u_texture, v_texcoord);
vec4 alphaColor = texture2D(u_alpha_texture, v_texcoord);
vec4 maskColor = texture2D(u_mask_texture, v_texcoord);
if (texColor.w < ` + GLDrawAlphaThreshold + `) discard;
if (alphaColor.w < ` + GLDrawAlphaThreshold + `) discard;
if (maskColor.w < ` + GLDrawAlphaThreshold + `) discard;
float t = (texColor.x + texColor.y + texColor.z) / 383.0;
if (t < ` + GLDrawHalfAlphaLow + ` || t > ` + GLDrawHalfAlphaHigh + `) {
gl_FragColor = texColor;
} else {
gl_FragColor = u_color * vec4(t, t, t, texColor.w);
}
if (texColor.w < ` + GLDrawAlphaThreshold + `) discard;
if (alphaColor.w < ` + GLDrawAlphaThreshold + `) discard;
if (maskColor.w < ` + GLDrawAlphaThreshold + `) discard;
float t = (texColor.x + texColor.y + texColor.z) / 383.0;
if (t < ` + GLDrawHalfAlphaLow + ` || t > ` + GLDrawHalfAlphaHigh + `) {
gl_FragColor = texColor;
} else {
gl_FragColor = u_color * vec4(t, t, t, texColor.w);
}
gl_FragColor.a *= maskColor.w;
}
`;
@ -675,23 +675,23 @@ function GLDrawLoadMask(gl, texWidth, texHeight, offsetX, offsetY, alphaMasks) {
* @returns {WebGLTexture} - A default mask texture
*/
function GLDrawCreateEmptyTextureAlphaMask(gl, texWidth, texHeight) {
const key = "EmptyTexMask:" + texWidth + "x" + texHeight;
let mask = gl.maskCache.get(key);
if (!mask) {
// Create a white 1x1 texture (fully visible)
const data = new Uint8Array([255, 255, 255, 255]);
mask = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, mask);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.maskCache.set(key, mask);
}
return mask;
const key = "EmptyTexMask:" + texWidth + "x" + texHeight;
let mask = gl.maskCache.get(key);
if (!mask) {
// Create a white 1x1 texture (fully visible)
const data = new Uint8Array([255, 255, 255, 255]);
mask = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, mask);
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.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.maskCache.set(key, mask);
}
return mask;
}
function canvasToNewWin(canvas) {
@ -708,7 +708,7 @@ function canvasToNewWin(canvas) {
/**
* Loads mask layers and combines them into a single texture mask
* @param {WebGL2RenderingContext} gl
* @param {WebGL2RenderingContext} gl
* @param {number} texWidth - The width of the texture
* @param {number} texHeight - The height of the texture
* @param {number} offsetX - The X offset for the texture
@ -718,7 +718,7 @@ function canvasToNewWin(canvas) {
*/
function GLDrawLoadTextureAlphaMask(gl, texWidth, texHeight, offsetX, offsetY, maskLayers) {
// If no mask layers, return empty texture or null
if (!maskLayers || maskLayers.length === 0
if (!maskLayers || maskLayers.length === 0
// placeholder texures
|| (texWidth === 1 && texHeight === 1)) {
return GLDrawCreateEmptyTextureAlphaMask(gl, texWidth, texHeight);
@ -738,11 +738,11 @@ function GLDrawLoadTextureAlphaMask(gl, texWidth, texHeight, offsetX, offsetY, m
const ctx = tmpCanvas.getContext("2d");
ctx.fillStyle = "rgb(0,0,0)";
ctx.fillRect(0, 0, texWidth, texHeight);
// Process and draw each mask layer
// Process and draw each mask layer
// Combining textures is just too heavy for this scenario
// so a canvas ctx is used to combine them
for (const layer of maskLayers) {
for (const layer of maskLayers) {
GLDrawLoadImage(gl, layer.Url);
const img = GLDrawImageCache.get(layer.Url);
if(img.width === 0 || img.height === 0) {
@ -751,7 +751,7 @@ function GLDrawLoadTextureAlphaMask(gl, texWidth, texHeight, offsetX, offsetY, m
ctx.globalCompositeOperation = layer.Mode || "destination-in";
ctx.drawImage(img, layer.X - offsetX, layer.Y - offsetY, img.width, img.height);
}
}
mask = gl.createTexture();
@ -761,8 +761,8 @@ function GLDrawLoadTextureAlphaMask(gl, texWidth, texHeight, offsetX, offsetY, m
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, tmpCanvas);
// Cache the generated mask
gl.maskCache.set(key, mask);
// Cache the generated mask
gl.maskCache.set(key, mask);
}
return mask;

View file

@ -3855,6 +3855,8 @@ interface TextureAlphaMask {
Y: number;
Url: string;
Mode: "destination-in" | "destination-out";
/** The priority of the mask, -1 means it will ignores priority and applied to the layers on top of it */
Priority: number;
}
/** Options available to most draw calls */