Merge pull request #987 from mozilla/add-image-transparency-setting

Add "alpha mode" setting for images
This commit is contained in:
Dominick D'Aniello 2020-07-07 15:02:58 -07:00 коммит произвёл GitHub
Родитель d75127abce cd02c5be7c
Коммит e821a5f0fa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 84 добавлений и 13 удалений

Просмотреть файл

@ -1,5 +1,5 @@
import EditorNodeMixin from "./EditorNodeMixin";
import Image from "../objects/Image";
import Image, { ImageAlphaMode } from "../objects/Image";
import spokeLogoSrc from "../../assets/spoke-icon.png";
import { RethrownError } from "../utils/errors";
import { getObjectPerfIssues, maybeAddLargeFileIssue } from "../utils/performance";
@ -16,12 +16,14 @@ export default class ImageNode extends EditorNodeMixin(Image) {
static async deserialize(editor, json, loadAsync, onError) {
const node = await super.deserialize(editor, json);
const { src, projection, controls } = json.components.find(c => c.name === "image").props;
const { src, projection, controls, alphaMode, alphaCutoff } = json.components.find(c => c.name === "image").props;
loadAsync(
(async () => {
await node.load(src, onError);
node.controls = controls || false;
node.alphaMode = alphaMode === undefined ? ImageAlphaMode.Blend : alphaMode;
node.alphaCutoff = alphaCutoff === undefined ? 0.5 : alphaCutoff;
node.projection = projection;
})()
);
@ -103,6 +105,8 @@ export default class ImageNode extends EditorNodeMixin(Image) {
super.copy(source, recursive);
this.controls = source.controls;
this.alphaMode = source.alphaMode;
this.alphaCutoff = source.alphaCutoff;
this._canonicalUrl = source._canonicalUrl;
return this;
@ -113,6 +117,8 @@ export default class ImageNode extends EditorNodeMixin(Image) {
image: {
src: this._canonicalUrl,
controls: this.controls,
alphaMode: this.alphaMode,
alphaCutoff: this.alphaCutoff,
projection: this.projection
}
});
@ -120,11 +126,18 @@ export default class ImageNode extends EditorNodeMixin(Image) {
prepareForExport() {
super.prepareForExport();
this.addGLTFComponent("image", {
const imageData = {
src: this._canonicalUrl,
controls: this.controls,
alphaMode: this.alphaMode,
projection: this.projection
});
};
if (this.alphaMode === ImageAlphaMode.Mask) {
imageData.alphaCutoff = this.alphaCutoff;
}
this.addGLTFComponent("image", imageData);
this.addGLTFComponent("networked", {
id: this.uuid
});

Просмотреть файл

@ -6,7 +6,6 @@ import {
Mesh,
sRGBEncoding,
LinearFilter,
RGBAFormat,
PlaneBufferGeometry
} from "three";
import loadTexture from "../utils/loadTexture";
@ -16,15 +15,25 @@ export const ImageProjection = {
Equirectangular360: "360-equirectangular"
};
export const ImageAlphaMode = {
Opaque: "opaque",
Blend: "blend",
Mask: "mask"
};
export default class Image extends Object3D {
constructor() {
super();
this._src = null;
this._projection = "flat";
this._alphaMode = ImageAlphaMode.Opaque;
this._alphaCutoff = 0.5;
const geometry = new PlaneBufferGeometry();
const material = new MeshBasicMaterial();
material.side = DoubleSide;
material.transparent = this.alphaMode === ImageAlphaMode.Blend;
material.alphaTest = this.alphaMode === ImageAlphaMode.Mask ? this._alphaCutoff : 0;
this._mesh = new Mesh(geometry, material);
this._mesh.name = "ImageMesh";
this.add(this._mesh);
@ -43,6 +52,27 @@ export default class Image extends Object3D {
return loadTexture(src);
}
get alphaMode() {
return this._alphaMode;
}
set alphaMode(v) {
this._alphaMode = v;
this._mesh.material.transparent = v === ImageAlphaMode.Blend;
this._mesh.material.alphaTest = v === ImageAlphaMode.Mask ? this.alphaCutoff : 0;
this._mesh.material.needsUpdate = true;
}
get alphaCutoff() {
return this._alphaCutoff;
}
set alphaCutoff(v) {
this._alphaCutoff = v;
this._mesh.material.alphaTest = v;
this._mesh.material.needsUpdate = true;
}
get projection() {
return this._projection;
}
@ -63,9 +93,8 @@ export default class Image extends Object3D {
material.map = this._texture;
if (this._texture && this._texture.format === RGBAFormat) {
material.transparent = true;
}
material.transparent = this.alphaMode === ImageAlphaMode.Blend;
material.alphaTest = this.alphaMode === ImageAlphaMode.Mask ? this._alphaCutoff : 0;
this._projection = projection;
@ -106,9 +135,8 @@ export default class Image extends Object3D {
this.onResize();
if (texture.format === RGBAFormat) {
this._mesh.material.transparent = true;
}
material.transparent = this.alphaMode === ImageAlphaMode.Blend;
material.alphaTest = this.alphaMode === ImageAlphaMode.Mask ? this._alphaCutoff : 0;
this._mesh.material.map = this._texture;
this._mesh.material.needsUpdate = true;

Просмотреть файл

@ -50,6 +50,8 @@ export const TooltipContainer = styled.div`
overflow-wrap: break-word;
user-select: none;
text-align: center;
white-space: pre-wrap;
}
`;
export function InfoTooltip({ info, children, ...rest }) {

Просмотреть файл

@ -4,18 +4,23 @@ import NodeEditor from "./NodeEditor";
import InputGroup from "../inputs/InputGroup";
import SelectInput from "../inputs/SelectInput";
import BooleanInput from "../inputs/BooleanInput";
import { ImageProjection } from "../../editor/objects/Image";
import NumericInputGroup from "../inputs/NumericInputGroup";
import { ImageProjection, ImageAlphaMode } from "../../editor/objects/Image";
import ImageInput from "../inputs/ImageInput";
import { Image } from "styled-icons/fa-solid/Image";
import useSetPropertySelected from "./useSetPropertySelected";
const imageProjectionOptions = Object.values(ImageProjection).map(v => ({ label: v, value: v }));
const mapValue = v => ({ label: v, value: v });
const imageProjectionOptions = Object.values(ImageProjection).map(mapValue);
const imageTransparencyOptions = Object.values(ImageAlphaMode).map(mapValue);
export default function ImageNodeEditor(props) {
const { editor, node } = props;
const onChangeSrc = useSetPropertySelected(editor, "src");
const onChangeControls = useSetPropertySelected(editor, "controls");
const onChangeProjection = useSetPropertySelected(editor, "projection");
const onChangeTransparencyMode = useSetPropertySelected(editor, "alphaMode");
const onChangeAlphaCutoff = useSetPropertySelected(editor, "alphaCutoff");
return (
<NodeEditor description={ImageNodeEditor.description} {...props}>
@ -25,6 +30,29 @@ export default function ImageNodeEditor(props) {
<InputGroup name="Controls" info="Toggle the visibility of the media controls in Hubs.">
<BooleanInput value={node.controls} onChange={onChangeControls} />
</InputGroup>
<InputGroup
name="Transparency Mode"
info={`How to apply transparency:
'opaque' = no transparency
'blend' = use the images alpha channel
'mask' = Use a specified cutoff value for on/off transparency (more performant)
`}
>
<SelectInput options={imageTransparencyOptions} value={node.alphaMode} onChange={onChangeTransparencyMode} />
</InputGroup>
{node.alphaMode === ImageAlphaMode.Mask && (
<NumericInputGroup
name="Alpha Cutoff"
info="Pixels with alpha values lower than this will be transparent"
min={0}
max={1}
smallStep={0.01}
mediumStep={0.1}
largeStep={0.25}
value={node.alphaCutoff}
onChange={onChangeAlphaCutoff}
/>
)}
<InputGroup name="Projection">
<SelectInput options={imageProjectionOptions} value={node.projection} onChange={onChangeProjection} />
</InputGroup>