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

3D Tiles - Support vectors in the styling language #4759

Merged
merged 4 commits into from
Jan 6, 2017
Merged

Conversation

lilleyse
Copy link
Contributor

@lilleyse lilleyse commented Dec 15, 2016

For CesiumGS/3d-tiles#2 and #3241

This adds support for vec2, vec3, and vec4 types in the styling language.

Under the hood all vector types are treated as Cartesian4, with missing components set to 0.0. Vector components may be accessed with .x, .y, .z, .w and [0], [1], [2], [3]. Colors components can also be accessed like this too in addition to .red, .green, .blue, and .alpha.

The following operators are supported for vectors:
Unary +, unary -, +, - *, /, %, ===, ==, !==, !=

The following operators are not supported:
<, <, >=, <=, &&, ||, !~, =~

To-do:

@@ -928,6 +1029,10 @@ define([
var right = this._right.evaluate(frameState, feature);
if ((right instanceof Color) && (left instanceof Color)) {
return Color.mod(left, right, ScratchStorage.getColor());
} else if ((right instanceof Cartesian4) && (left instanceof Cartesian4)) {
// TODO : make mod a built-in Cartesian function?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you, but I'm not sure it is worth it until/less we have another use case since we would also have to add it to Cartesian2, Cartesian3, etc.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

Under the hood all vector types are treated as Cartesian4

The memory usage here is fine, but I am not thrilled with the potential compute overhead when evaluating expressions on the CPU. Perhaps don't worry about it now, but add a PERFORMANCE_IDEA comment.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

Since these look like GLSL types, can the spec please have a short section to explicitly say how the types are different than GLSL? For example, they do not support swizzling (and you'll have to decide if vec4(1.0), for example, is allowed; it is OK with me).

Also, does the GLSL implementation allow styles that are not valid, e.g., swizzling, since it is handed off to the GLSL compiler? If so, we should explicitly catch these cases.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

For the spec, what happens if the index is out of bounds when accessing the vector with array notation?

expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
});

it('evaluates color properties (["red"], ["green"], ["blue"], ["alpha"])', function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the use case for supporting this? color[${CHANNEL}]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for color('yellow')['red'] equivalent to color('yellow').red.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, I see what it does, I'm just asking why since GLSL does not allow this since it does not have strings. How would the GLSL backend handle color[${CHANNEL}]?

If there are compelling use cases, we can keep this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just treating ["red"] as a special case, but yeah other strings like color[${CHANNEL}] will not work. I am fine with disallowing ["red"] in GLSL for consistency.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean by disallowing. I simply mean that arrays should be indexed with integers, not strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accessing color components like this is supported in the spec:

Bracket notation ([]) is also used to access properties, e.g., color['red'], or arrays, e.g., temperatures[1].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right. OK, disallow and document for now.

expect(expression.evaluate(frameState, undefined)).toEqual(0.5);
});

it('evaluates color properties (["x"], ["y"], ["z"], ["w"])', function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For vec4(1,1,1,1)['x'] equivalent to vec4(1,1,1,1).x

});

it('evaluates expression with multiple nested vectors', function() {
var expression = new Expression('vec4(vec2(1, 2)[vec3(6, 1, 5).y], 2, vec4().w, 5)');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also for the spec, is construction like vec3(vec2(1.0, 2.0), 3.0) allows? Up to you, just explicitly state it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not allowed, all components must evaluate to numbers (which the example above does). I will make this clear in the spec.

expression = new Expression('-vec3(1, 2, 3)');
expect(expression.evaluate(frameState, undefined)).toEqual(new Cartesian4(-1, -2, -3, 0));

expression = new Expression('vec2(1, 2) + vec4(3, 4, 5, 6)');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere, does GLSL allow this? I think this should be more strictly typed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GLSL does not allow it by default - but all vector types are turned into vec4 so this is valid. There are a lot of complications in recognizing vec2, vec3, vec4 type mismatches otherwise. I will add a note in the spec that vec2, vec3, and vec4 are just different constructors for the vector type - which is always vec4.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you are letting this specific implementation too strongly influence the spec. Would a language really define that all vector types map to vec4?

There are a lot of complications in recognizing vec2, vec3, vec4 type mismatches...

What are they?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the JS side it isn't too bad, just that operations between different Cartesian types will result in NaN. In GLSL the shader will not compile due to type mismatches (which cannot be verified ahead since properties are not known ... like color : ${id} > 10 ? vec3(1.0) : vec2(2.0)).

Having everything be the same type makes it easier to do operations between vector properties and literal vectors without needing to support special constructors like vec4(${rgbcolor}, 1.0).

What about dropping vec2 and vec3 and always using vec4?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about dropping vec2 and vec3 and always using vec4?

We could see how much milage we get from this.

Remind me, why are we adding vector types at all right now? To flush out the math library with things like dot and cross products? If that is the case, don't we really need a vec3.

I am also still not convinced that we can't reasonably detect this even if some error messages have to be "Could not compile generated GLSL shader, this is often due to a vector type mismatch."

Copy link
Contributor Author

@lilleyse lilleyse Dec 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The vector types are needed to support vector properties in the batch table binary, which can be vec2, vec3, vec4, or accessing point positions with ${POSITION} as well as math operations. Having vec4 as the base type also makes it easier to interact with colors (should colors and vectors mix?).

In line with other vector libraries I can support vec2, vec3, and vec4 as distinct types - however it will add more complexity.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

Looks good, just those comments.

@lilleyse
Copy link
Contributor Author

(and you'll have to decide if vec4(1.0), for example, is allowed; it is OK with me).

This is allowed, but it will result in vec4(1,0,0,0) instead of vec4(1,1,1,1). I'll make it clear in the spec that unset components default to 0.

Also, does the GLSL implementation allow styles that are not valid, e.g., swizzling, since it is handed off to the GLSL compiler? If so, we should explicitly catch these cases.

The GLSL implementation catches these cases and will throw a developer error.

For the spec, what happens if the index is out of bounds when accessing the vector with array notation?

Hm... good point - the current behavior is to return undefined in the JS backend and not compile in the GLSL backend.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

(and you'll have to decide if vec4(1.0), for example, is allowed; it is OK with me).

This is allowed, but it will result in vec4(1,0,0,0) instead of vec4(1,1,1,1). I'll make it clear in the spec that unset components default to 0.

As I mentioned above, I don't think this is a good idea at the spec level.

For the spec, what happens if the index is out of bounds when accessing the vector with array notation?

Hm... good point - the current behavior is to return undefined in the JS backend and not compile in the GLSL backend.

Can we be consistent? Can variables be passed at runtime as array indices? If not, it is probably easy enough to catch this at compile time with GLSL and map undefined to 0.0 or whatever we decided for GLSL.

@lilleyse
Copy link
Contributor Author

As I mentioned above, I don't think this is a good idea at the spec level.

Do you mean the spec should have it become vec4(1,1,1,1) or that this decision is too specific for the spec?

Can we be consistent? Can variables be passed at runtime as array indices? If not, it is probably easy enough to catch this at compile time with GLSL and map undefined to 0.0 or whatever we decided for GLSL.

Yes variables can be passed at runtime so checking at compile-time is not possible.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 18, 2016

As I mentioned above, I don't think this is a good idea at the spec level.

Do you mean the spec should have it become vec4(1,1,1,1) or that this decision is too specific for the spec?

Yes, I would do vec4(1,1,1,1) like GLSL. The implicit mapping of all vector types to vec4 is just not at all common in the, I dunno, dozens of vector libraries I have seen. We should mirror GLSL as close as possible.

Can we be consistent? Can variables be passed at runtime as array indices? If not, it is probably easy enough to catch this at compile time with GLSL and map undefined to 0.0 or whatever we decided for GLSL.

Yes variables can be passed at runtime so checking at compile-time is not possible.

If the JavaScript and GLSL implementations need to be different, please record the details in CesiumGS/3d-tiles#140. We have to be very diligent and precise; otherwise, the styling ecosystem will not be robust.

@lilleyse
Copy link
Contributor Author

Yes, I would do vec4(1,1,1,1) like GLSL. The implicit mapping of all vector types to vec4 is just not at all common in the, I dunno, dozens of vector libraries I have seen. We should mirror GLSL as close as possible.

Sounds good to me. I'll follow GLSL more closely.

Yes variables can be passed at runtime so checking at compile-time is not possible.
If the JavaScript and GLSL implementations need to be different, please record the details in CesiumGS/3d-tiles#140. We have to be very diligent and precise; otherwise, the styling ecosystem will not be robust.

Added a note to the list.

@lilleyse
Copy link
Contributor Author

lilleyse commented Jan 6, 2017

Update to support vec2, vec3, and vec4 as distinct types. The constructors follow the same rules as glsl, so some valid constructors are:

vec4(1)
vec3(1, 2, 3)
vec3(vec4(1, 2, 3, 4))
vec4(vec3(1, 2, 3), 4)
vec4(vec2(1, 2), vec2(3, 4))
etc...

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 6, 2017

Do you want to move the tasklist in the first comment here to another roadmap? Merging this without finishing them is OK with me.

// For example vec3(1, 2, 3) or vec3(vec2(1, 2), 3) are both valid.
//
// If the number of components does not equal the vector's size, then a DeveloperError is thrown - with two exceptions:
// 1. A vector may be constructed from a larger vector and drop the extra components.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is (1) also true in GLSL?

If not, is it hard/slow to implement the same behavior here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merging, we'll open a separate PR if this needs a code change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah (1) is true in GLSL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants