зеркало из https://github.com/mozilla/Spoke.git
Merge pull request #987 from mozilla/add-image-transparency-setting
Add "alpha mode" setting for images
This commit is contained in:
Коммит
e821a5f0fa
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче