Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

View independent transparency override #2692

Merged
merged 12 commits into from
Nov 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions common/api/core-common.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2914,32 +2914,28 @@ export class Feature {
}

// @public
export class FeatureAppearance implements FeatureAppearanceProps {
export class FeatureAppearance {
protected constructor(props: FeatureAppearanceProps);
get anyOverridden(): boolean;
clone(changedProps: FeatureAppearanceProps): FeatureAppearance;
cloneProps(changedProps: FeatureAppearanceProps): FeatureAppearanceProps;
static readonly defaults: FeatureAppearance;
// (undocumented)
readonly emphasized?: true | undefined;
readonly emphasized?: true;
// (undocumented)
equals(other: FeatureAppearance): boolean;
extendAppearance(base: FeatureAppearance): FeatureAppearance;
// (undocumented)
static fromJSON(props?: FeatureAppearanceProps): FeatureAppearance;
static fromRgb(color: ColorDef): FeatureAppearance;
static fromRgba(color: ColorDef): FeatureAppearance;
static fromRgba(color: ColorDef, viewDependentTransparency?: boolean): FeatureAppearance;
static fromSubCategoryOverride(ovr: SubCategoryOverride): FeatureAppearance;
static fromTransparency(transparencyValue: number): FeatureAppearance;
// (undocumented)
readonly ignoresMaterial?: true | undefined;
static fromTransparency(transparencyValue: number, viewDependent?: boolean): FeatureAppearance;
readonly ignoresMaterial?: true;
// (undocumented)
get isFullyTransparent(): boolean;
// (undocumented)
readonly linePixels?: LinePixels;
get matchesDefaults(): boolean;
// (undocumented)
readonly nonLocatable?: true | undefined;
readonly nonLocatable?: true;
// (undocumented)
get overridesLinePixels(): boolean;
// (undocumented)
Expand All @@ -2952,24 +2948,23 @@ export class FeatureAppearance implements FeatureAppearanceProps {
get overridesTransparency(): boolean;
// (undocumented)
get overridesWeight(): boolean;
// (undocumented)
readonly rgb?: RgbColor;
// (undocumented)
toJSON(): FeatureAppearanceProps;
// (undocumented)
readonly transparency?: number;
// (undocumented)
readonly viewDependentTransparency?: true;
readonly weight?: number;
}

// @public
export interface FeatureAppearanceProps {
emphasized?: true | undefined;
ignoresMaterial?: true | undefined;
emphasized?: true;
ignoresMaterial?: true;
linePixels?: LinePixels;
nonLocatable?: true | undefined;
nonLocatable?: true;
rgb?: RgbColorProps;
transparency?: number;
viewDependentTransparency?: true;
weight?: number;
}

Expand Down
2 changes: 1 addition & 1 deletion common/api/summary/core-common.exports.csv
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ beta;ExternalSourceAttachmentProps
beta;ExternalSourceAttachmentRole
beta;ExternalSourceProps
public;Feature
public;FeatureAppearance
public;FeatureAppearance
public;FeatureAppearanceProps
public;FeatureAppearanceProvider
public;FeatureAppearanceProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/core-common",
"comment": "FeatureAppearance transparency override by default ignores render mode and transparency view flag.",
"type": "none"
}
],
"packageName": "@itwin/core-common"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/core-frontend",
"comment": "FeatureAppearance transparency by default ignores render mode and transparency view flag.",
"type": "none"
}
],
"packageName": "@itwin/core-frontend"
}
76 changes: 54 additions & 22 deletions core/common/src/FeatureSymbology.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,55 @@ function copyIdSetToUint32Set(dst: Id64.Uint32Set, src: Iterable<string>): void

// cspell:ignore subcat subcats

/** Properties used to initialize a [[FeatureAppearance]].
/** JSON representation of a [[FeatureAppearance]].
* @public
*/
export interface FeatureAppearanceProps {
/** The color of the Feature */
/** @see [[FeatureAppearance.rgb]]. */
rgb?: RgbColorProps;
/** The line weight in pixels as an integer in [1, 31] */
/** @see [[FeatureAppearance.weight]]. */
weight?: number;
/** The transparency in the range [0.0, 1.0] where 0 indicates fully opaque and 1 indicates fully transparent. */
/** @see [[FeatureAppearance.transparency]]. */
transparency?: number;
/** The pixel pattern used to draw lines. */
/** @see [[FeatureAppearance.viewDependentTransparency]]. */
viewDependentTransparency?: true;
/** @see [[FeatureAppearance.linePixels]]. */
linePixels?: LinePixels;
/** If true, ignore the [[RenderMaterial]] associated with surfaces. */
ignoresMaterial?: true | undefined;
/** If true, the associated [[Feature]] will not be drawn when using [Viewport.readPixels]($frontend). */
nonLocatable?: true | undefined;
/** If true, the associated [[Feature]] will be emphasized. Emphasized features are rendered using the [[Hilite.Settings]] defined by [Viewport.emphasisSettings]($frontend). */
emphasized?: true | undefined;
/** @see [[FeatureAppearance.ignoresMaterial]]. */
ignoresMaterial?: true;
/** @see [[FeatureAppearance.nonLocatable]]. */
nonLocatable?: true;
/** @see [[FeatureAppearance.emphasized]]. */
emphasized?: true;
}

/** Defines overrides for selected aspects of a [[Feature]]'s symbology.
* Any member defined in the appearance overrides that aspect of symbology for all [[Feature]]s to which the appearance is applied.
* @see [[FeatureOverrides]] to customize the appearance of multiple features.
* @public
*/
export class FeatureAppearance implements FeatureAppearanceProps {
export class FeatureAppearance {
/** Overrides the feature's color. */
public readonly rgb?: RgbColor;
/** The width of lines and edges in pixels as an integer in [1, 31]. */
public readonly weight?: number;
/** The transparency in the range [0, 1] where 0 indicates fully opaque and 1 indicates fully transparent.
* @see [[viewDependentTransparency]] for details on how this override interacts with the [DisplayStyle]($backend).
*/
public readonly transparency?: number;
/** The pixel pattern applied to lines and edges. */
public readonly linePixels?: LinePixels;
public readonly ignoresMaterial?: true | undefined;
public readonly nonLocatable?: true | undefined;
public readonly emphasized?: true | undefined;
/** If true, don't apply the [[RenderMaterial]] to the feature's surfaces. */
public readonly ignoresMaterial?: true;
/** If true, the feature will not be drawn when using [Viewport.readPixels]($frontend), meaning [Tool]($frontend)s will not be able to interact with it. */
public readonly nonLocatable?: true;
/** If true, the feature will be rendered using the [[Hilite.Settings]] defined by [Viewport.emphasisSettings]($frontend) to make it stand out. */
public readonly emphasized?: true;
/** If true, then [[transparency]] will only be applied if [[ViewFlags.transparency]] is enabled and the current [[RenderMode]] supports transparency.
* Default: false, meaning the transparency will be applied regardless of view flags or render mode.
* This property has no effect if [[transparency]] is `undefined`.
*/
public readonly viewDependentTransparency?: true;

/** An appearance that overrides nothing. */
public static readonly defaults = new FeatureAppearance({});
Expand All @@ -80,24 +96,30 @@ export class FeatureAppearance implements FeatureAppearanceProps {
/** Create a FeatureAppearance that overrides the RGB and transparency.
* The appearance's transparency is derived from the transparency component of the ColorDef.
*/
public static fromRgba(color: ColorDef): FeatureAppearance {
public static fromRgba(color: ColorDef, viewDependentTransparency = false): FeatureAppearance {
return this.fromJSON({
rgb: RgbColor.fromColorDef(color),
transparency: color.colors.t / 255,
viewDependentTransparency: viewDependentTransparency ? true : undefined,
});
}
/** Create a FeatureAppearance that overrides only the transparency */
public static fromTransparency(transparencyValue: number): FeatureAppearance {
return this.fromJSON({ transparency: transparencyValue });
public static fromTransparency(transparencyValue: number, viewDependent = false): FeatureAppearance {
return this.fromJSON({
transparency: transparencyValue,
viewDependentTransparency: viewDependent ? true : undefined,
});
}

/** Create a FeatureAppearance with overrides corresponding to those defined by the supplied SubCategoryOverride. */
/** Create a FeatureAppearance with overrides corresponding to those defined by the supplied SubCategoryOverride.
* @note Subcategory overrides set [[viewDependentTransparency]] to `true`.
*/
public static fromSubCategoryOverride(ovr: SubCategoryOverride): FeatureAppearance {
const rgb = undefined !== ovr.color ? RgbColor.fromColorDef(ovr.color) : undefined;
const transparency = ovr.transparency;
const weight = ovr.weight;
const ignoresMaterial = undefined !== ovr.material && Id64.isValid(ovr.material) ? true : undefined;
return this.fromJSON({ rgb, transparency, weight, ignoresMaterial });
return this.fromJSON({ rgb, transparency, weight, ignoresMaterial, viewDependentTransparency: true });
}

/** Returns true if this appearance does not override any aspects of symbology. */
Expand Down Expand Up @@ -128,7 +150,8 @@ export class FeatureAppearance implements FeatureAppearanceProps {
&& this.linePixels === other.linePixels
&& this.ignoresMaterial === other.ignoresMaterial
&& this.nonLocatable === other.nonLocatable
&& this.emphasized === other.emphasized;
&& this.emphasized === other.emphasized
&& this.viewDependentTransparency === other.viewDependentTransparency;
}

public toJSON(): FeatureAppearanceProps {
Expand All @@ -139,8 +162,11 @@ export class FeatureAppearance implements FeatureAppearanceProps {
if (undefined !== this.weight)
props.weight = this.weight;

if (undefined !== this.transparency)
if (undefined !== this.transparency) {
props.transparency = this.transparency;
if (this.viewDependentTransparency)
props.viewDependentTransparency = true;
}

if (undefined !== this.linePixels)
props.linePixels = this.linePixels;
Expand Down Expand Up @@ -198,6 +224,9 @@ export class FeatureAppearance implements FeatureAppearanceProps {
if (undefined === props.nonLocatable && this.nonLocatable) props.nonLocatable = true;
if (undefined === props.emphasized && this.emphasized) props.emphasized = true;

if (undefined !== props.transparency && this.viewDependentTransparency)
props.viewDependentTransparency = true;

return FeatureAppearance.fromJSON(props);
}

Expand All @@ -214,6 +243,9 @@ export class FeatureAppearance implements FeatureAppearanceProps {
this.weight = Math.max(1, Math.min(this.weight, 32));

if (undefined !== this.transparency) {
if (props.viewDependentTransparency)
this.viewDependentTransparency = true;

this.transparency = Math.max(0, Math.min(this.transparency, 1));

// Fix up rounding errors...
Expand Down
38 changes: 38 additions & 0 deletions core/common/src/test/FeatureSymbology.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,44 @@ describe("FeatureAppearance", () => {
test({ transp: 0.5 }, { transparency: 0.5 });
test({ transp: 1.0 }, { transparency: 1.0 });
});

it("view-dependent transparency", () => {
it("to and from JSON", () => {
function test(appProps: FeatureAppearanceProps, expectViewDependent: boolean): void {
const expected = expectViewDependent ? true : undefined;
const app = FeatureAppearance.fromJSON(appProps);
expect(app.viewDependentTransparency).to.equal(expected);
expect(app.toJSON().viewDependentTransparency).to.equal(expected);
}

test({ }, false);
test({ transparency: undefined }, false);
test({ transparency: 1 }, false);
test({ transparency: 0 }, false );

test({ transparency: 1, viewDependentTransparency: true }, true);
test({ transparency: 0, viewDependentTransparency: true }, true);

test({ viewDependentTransparency: true }, false);
test({ transparency: undefined, viewDependentTransparency: true }, false);
});

it("from subcategory override", () => {
function test(ovrProps: SubCategoryAppearance.Props, expectViewDependent: boolean): void {
const expected = expectViewDependent ? true : undefined;
const ovr = SubCategoryOverride.fromJSON(ovrProps);
const app = FeatureAppearance.fromSubCategoryOverride(ovr);
expect(app.viewDependentTransparency).to.equal(expected);
expect(app.toJSON().viewDependentTransparency).to.equal(expected);
}

test({ transp: 0.5 }, true);
test({ transp: 0 }, true);
test({ transp: undefined }, false);
test({ }, false);
test({ color: ColorDef.blue.toJSON() }, false);
});
});
});

describe("FeatureOverrides", () => {
Expand Down
13 changes: 10 additions & 3 deletions core/frontend/src/render/webgl/FeatureOverrides.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class FeatureOverrides implements WebGLDisposable {
private _anyOverridden = true;
private _allHidden = true;
private _anyTranslucent = true;
private _anyViewIndependentTranslucent = true;
private _anyOpaque = true;
private _anyHilited = true;
private _lutParams = new Float32Array(2);
Expand All @@ -83,6 +84,7 @@ export class FeatureOverrides implements WebGLDisposable {
public get anyOverridden() { return this._anyOverridden; }
public get allHidden() { return this._allHidden; }
public get anyTranslucent() { return this._anyTranslucent; }
public get anyViewIndependentTranslucent() { return this._anyViewIndependentTranslucent; }
public get anyOpaque() { return this._anyOpaque; }
public get anyHilited() { return this._anyHilited; }

Expand Down Expand Up @@ -148,7 +150,7 @@ export class FeatureOverrides implements WebGLDisposable {
const modelIdParts = Id64.getUint32Pair(map.modelId);
const isModelHilited = allowHilite && hilites.models.has(modelIdParts.lower, modelIdParts.upper);

this._anyOpaque = this._anyTranslucent = this._anyHilited = false;
this._anyOpaque = this._anyTranslucent = this._anyViewIndependentTranslucent = this._anyHilited = false;

let nHidden = 0;
let nOverridden = 0;
Expand Down Expand Up @@ -213,10 +215,15 @@ export class FeatureOverrides implements WebGLDisposable {
alpha = 0xff;

data.setByteAtIndex(dataIndex + 7, alpha);
if (0xff === alpha)
if (0xff === alpha) {
this._anyOpaque = true;
else
} else {
this._anyTranslucent = true;
if (!app.viewDependentTransparency) {
flags |= OvrFlags.ViewIndependentTransparency;
this._anyViewIndependentTranslucent = true;
}
}
}

if (app.overridesWeight && app.weight) {
Expand Down
2 changes: 1 addition & 1 deletion core/frontend/src/render/webgl/RenderCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ export class RenderCommands implements Iterable<DrawCommands> {
this._batchState.push(batch, true);

this.pushAndPop(new PushBatchCommand(batch), PopBatchCommand.instance, () => {
if (this.currentViewFlags.transparency) {
if (this.currentViewFlags.transparency || overrides.anyViewIndependentTranslucent) {
this._opaqueOverrides = overrides.anyOpaque;
this._translucentOverrides = overrides.anyTranslucent;

Expand Down
1 change: 1 addition & 0 deletions core/frontend/src/render/webgl/RenderFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export const enum OvrFlags {
Weight = 1 << 7,
Hilited = 1 << 8,
Emphasized = 1 << 9, // rendered with "emphasis" hilite settings (silhouette etc).
ViewIndependentTransparency = 1 << 10,

Rgba = Rgb | Alpha,
}
Expand Down
Loading