Skip to content

Commit

Permalink
Initial KHR_animation_pointer support (#230)
Browse files Browse the repository at this point in the history
  • Loading branch information
lexaknyazev authored Aug 9, 2024
1 parent 95ed362 commit 4dcb6a3
Show file tree
Hide file tree
Showing 26 changed files with 958 additions and 9 deletions.
3 changes: 3 additions & 0 deletions ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
|EXTRA_PROPERTY|This property should not be defined as it will not be used.|Information|
|INVALID_EXTENSION_NAME_FORMAT|Extension name has invalid format.|Warning|
|INVALID_GL_VALUE|Invalid value `%1` for GL type '`%2`'.|Error|
|KHR_ANIMATION_POINTER_ANIMATION_CHANNEL_TARGET_NODE|This extension requires the animation channel target node to be undefined.|Error|
|KHR_ANIMATION_POINTER_ANIMATION_CHANNEL_TARGET_PATH|This extension requires the animation channel target path to be 'pointer'. Found '`%1`' instead.|Error|
|KHR_LIGHTS_PUNCTUAL_LIGHT_SPOT_ANGLES|outerConeAngle (`%2`) is less than or equal to innerConeAngle (`%1`).|Error|
|KHR_MATERIALS_ANISOTROPY_ANISOTROPY_TEXTURE_TEXCOORD|Normal and anisotropy textures should use the same texture coords.|Warning|
|KHR_MATERIALS_CLEARCOAT_CLEARCOAT_NORMAL_TEXTURE_TEXCOORD|Normal and clearcoat normal textures should use the same texture coords.|Warning|
Expand Down Expand Up @@ -97,6 +99,7 @@
|BUFFER_VIEW_TARGET_OVERRIDE|Override of previously set bufferView target or usage. Initial: '`%1`', new: '`%2`'.|Error|
|BUFFER_VIEW_TOO_LONG|BufferView does not fit buffer (`%1`) byteLength (`%2`).|Error|
|IMAGE_BUFFER_VIEW_WITH_BYTESTRIDE|bufferView.byteStride must not be defined for buffer views containing image data.|Error|
|INCOMPLETE_EXTENSION_SUPPORT|Validation support for this extension is incomplete; the asset may have undetected issues.|Information|
|INVALID_IBM_ACCESSOR_COUNT|IBM accessor must have at least `%1` elements. Found `%2`.|Error|
|KHR_MATERIALS_VARIANTS_NON_UNIQUE_VARIANT|This variant is used more than once for this mesh primitive.|Error|
|MESH_PRIMITIVE_ACCESSOR_UNALIGNED|Vertex attribute data must be aligned to 4-byte boundaries.|Error|
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ NuGet package [Third-party contribution]: https://www.nuget.org/packages/GltfVal
- Warning on unsupported image features (like animations or custom color spaces).
- Extensions validation
- EXT_texture_webp
- KHR_animation_pointer (partial)
- KHR_lights_punctual
- KHR_materials_anisotropy
- KHR_materials_clearcoat
Expand Down
13 changes: 10 additions & 3 deletions lib/src/base/animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,19 @@ class AnimationChannelTarget extends GltfProperty {
checkMembers(map, ANIMATION_CHANNEL_TARGET_MEMBERS, context);
}

return AnimationChannelTarget._(
final extensions = getExtensions(map, AnimationChannelTarget, context,
overriddenType: Animation);

final target = AnimationChannelTarget._(
getIndex(map, NODE, context, req: false),
getString(map, PATH, context,
req: true, list: ANIMATION_CHANNEL_TARGET_PATHS),
getExtensions(map, AnimationChannelTarget, context),
req: true, list: context.animationChannelTargetPaths),
extensions,
getExtras(map, context));

context.registerObjectsOwner(target, [...extensions.values]);

return target;
}

bool isSameAs(AnimationChannelTarget other) =>
Expand Down
2 changes: 2 additions & 0 deletions lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,8 @@ class Context {
const AccessorFormat(VEC4, gl.UNSIGNED_SHORT, normalized: true)
}
};

final animationChannelTargetPaths = [...ANIMATION_CHANNEL_TARGET_PATHS];
}

class IssuesLimitExceededException implements Exception {
Expand Down
18 changes: 18 additions & 0 deletions lib/src/errors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,18 @@ class SemanticError extends IssueType {
(args) => 'This property should not be defined as it will not be used.',
Severity.Information);

static final SemanticError khrAnimationPointerAnimationChannelTargetNode =
SemanticError._(
'KHR_ANIMATION_POINTER_ANIMATION_CHANNEL_TARGET_NODE',
(args) => 'This extension requires the animation channel target node '
'to be undefined.');

static final SemanticError khrAnimationPointerAnimationChannelTargetPath =
SemanticError._(
'KHR_ANIMATION_POINTER_ANIMATION_CHANNEL_TARGET_PATH',
(args) => 'This extension requires the animation channel target path '
"to be 'pointer'. Found ${_q(args[0])} instead.");

static final SemanticError khrLightsPunctualLightSpotAngles = SemanticError._(
'KHR_LIGHTS_PUNCTUAL_LIGHT_SPOT_ANGLES',
(args) => 'outerConeAngle (${args[1]}) is less than or equal to '
Expand Down Expand Up @@ -653,6 +665,12 @@ class LinkError extends IssueType {
(args) => 'bufferView.byteStride must not be defined for '
'buffer views containing image data.');

static final LinkError incompleteExtensionSupport = LinkError._(
'INCOMPLETE_EXTENSION_SUPPORT',
(args) => 'Validation support for this extension is incomplete; '
'the asset may have undetected issues.',
Severity.Information);

static final LinkError invalidIbmAccessorCount = LinkError._(
'INVALID_IBM_ACCESSOR_COUNT',
(args) => 'IBM accessor must have at least ${args[0]} elements.'
Expand Down
76 changes: 76 additions & 0 deletions lib/src/ext/KHR_animation_pointer/khr_animation_pointer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2024 The Khronos Group Inc.
//
// SPDX-License-Identifier: Apache-2.0

library gltf.extensions.khr_animation_pointer;

import 'package:gltf/src/base/gltf_property.dart';
import 'package:gltf/src/ext/extensions.dart';

const String KHR_ANIMATION_POINTER = 'KHR_animation_pointer';

const String POINTER = 'pointer';

const List<String> KHR_ANIMATION_POINTER_MEMBERS = <String>[
POINTER,
];

class KhrAnimationPointer extends GltfProperty {
final String pointer;

KhrAnimationPointer._(
this.pointer, Map<String, Object> extensions, Object extras)
: super(extensions, extras);

static final RegExp _pointerRegExp = RegExp(r'^(?:\/(?:[^/~]|~0|~1)*)*$');

static KhrAnimationPointer fromMap(Map<String, Object> map, Context context) {
if (context.validate) {
checkMembers(map, KHR_ANIMATION_POINTER_MEMBERS, context);
}

final pointer =
getString(map, POINTER, context, req: true, regexp: _pointerRegExp);

final extensions = getExtensions(map, KhrAnimationPointer, context);

return KhrAnimationPointer._(pointer, extensions, getExtras(map, context));
}

@override
void link(Gltf gltf, Context context) {
// TODO:
// * pointer existence
// * channel target uniqueness
// * output accessor compatibility
context.addIssue(LinkError.incompleteExtensionSupport);

Object o = this;
while (o != null) {
o = context.owners[o];
if (o is AnimationChannelTarget) {
if (o.node != null) {
context.addIssue(
SemanticError.khrAnimationPointerAnimationChannelTargetNode);
}
if (o.path != POINTER) {
context.addIssue(
SemanticError.khrAnimationPointerAnimationChannelTargetPath,
args: [o.path]);
}
break;
}
}
}
}

const Extension khrAnimationPointerExtension = Extension(
KHR_ANIMATION_POINTER,
<Type, ExtensionDescriptor>{
AnimationChannelTarget: ExtensionDescriptor(KhrAnimationPointer.fromMap)
},
init: _init);

void _init(Context context) {
context.animationChannelTargetPaths.add(POINTER);
}
3 changes: 3 additions & 0 deletions lib/src/ext/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ library gltf.extensions;

import 'package:gltf/src/base/gltf_property.dart';
import 'package:gltf/src/ext/EXT_texture_webp/ext_texture_webp.dart';
import 'package:gltf/src/ext/KHR_animation_pointer/khr_animation_pointer.dart';
import 'package:gltf/src/ext/KHR_lights_punctual/khr_lights_punctual.dart';
import 'package:gltf/src/ext/KHR_materials_anisotropy/khr_materials_anisotropy.dart';
import 'package:gltf/src/ext/KHR_materials_clearcoat/khr_materials_clearcoat.dart';
Expand All @@ -38,6 +39,7 @@ import 'package:gltf/src/hash.dart';
import 'package:meta/meta.dart';

export 'package:gltf/src/ext/EXT_texture_webp/ext_texture_webp.dart';
export 'package:gltf/src/ext/KHR_animation_pointer/khr_animation_pointer.dart';
export 'package:gltf/src/ext/KHR_lights_punctual/khr_lights_punctual.dart';
export 'package:gltf/src/ext/KHR_materials_anisotropy/khr_materials_anisotropy.dart';
export 'package:gltf/src/ext/KHR_materials_clearcoat/khr_materials_clearcoat.dart';
Expand Down Expand Up @@ -102,6 +104,7 @@ class ResourceValidatableExtensionEntry {

const List<Extension> kDefaultExtensions = <Extension>[
extTextureWebPExtension,
khrAnimationPointerExtension,
khrLightsPunctualExtension,
khrMaterialsAnisotropyExtension,
khrMaterialsClearcoatExtension,
Expand Down
15 changes: 15 additions & 0 deletions test/ext/KHR_animation_pointer/assets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"animation/channel/target": {
"name": "animation.channel.target.KHR_animation_pointer",
"tests": {
"invalid_channel_target_node.gltf": "Invalid channel target node",
"invalid_channel_target_path.gltf": "Invalid channel target path",
"invalid_pointer_syntax.gltf": "Invalid pointer syntax",
"missing_extension_declaration.gltf": "Missing extension declaration",
"missing_pointer.gltf": "Missing pointer string",
"custom_property.gltf": "Custom property",
"unexpected_extension.gltf": "Unexpected extension object location",
"valid.gltf": "Valid"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"asset": {
"version": "2.0"
},
"extensionsUsed": [
"KHR_animation_pointer"
],
"materials": [
{
"pbrMetallicRoughness": { }
}
],
"accessors": [
{
"count": 2,
"type": "SCALAR",
"componentType": 5126,
"min": [ 0 ],
"max": [ 1 ]
},
{
"count": 2,
"type": "VEC4",
"componentType": 5126
}
],
"animations": [
{
"channels": [
{
"target": {
"path": "pointer",
"extensions": {
"KHR_animation_pointer": {
"pointer": "/materials/0/pbrMetallicRoughness/baseColorFactor",
"customProperty": true
}
}
},
"sampler": 0
}
],
"samplers": [
{
"input": 0,
"output": 1
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"uri": "test/ext/KHR_animation_pointer/data/animation/channel/target/custom_property.gltf",
"mimeType": "model/gltf+json",
"validatorVersion": "2.0.0-dev.3.10",
"issues": {
"numErrors": 0,
"numWarnings": 1,
"numInfos": 2,
"numHints": 0,
"messages": [
{
"code": "UNEXPECTED_PROPERTY",
"message": "Unexpected property.",
"severity": 1,
"pointer": "/animations/0/channels/0/target/extensions/KHR_animation_pointer/customProperty"
},
{
"code": "INCOMPLETE_EXTENSION_SUPPORT",
"message": "Validation support for this extension is incomplete; the asset may have undetected issues.",
"severity": 2,
"pointer": "/animations/0/channels/0/target/extensions/KHR_animation_pointer"
},
{
"code": "UNUSED_OBJECT",
"message": "This object may be unused.",
"severity": 2,
"pointer": "/materials/0"
}
],
"truncated": false
},
"info": {
"version": "2.0",
"extensionsUsed": [
"KHR_animation_pointer"
],
"animationCount": 1,
"materialCount": 1,
"hasMorphTargets": false,
"hasSkins": false,
"hasTextures": false,
"hasDefaultScene": false,
"drawCallCount": 0,
"totalVertexCount": 0,
"totalTriangleCount": 0,
"maxUVs": 0,
"maxInfluences": 0,
"maxAttributes": 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"asset": {
"version": "2.0"
},
"extensionsUsed": [
"KHR_animation_pointer"
],
"accessors": [
{
"count": 2,
"type": "SCALAR",
"componentType": 5126,
"min": [ 0 ],
"max": [ 1 ]
},
{
"count": 2,
"type": "VEC4",
"componentType": 5126
}
],
"nodes": [
{ }
],
"animations": [
{
"channels": [
{
"target": {
"node": 0,
"path": "pointer",
"extensions": {
"KHR_animation_pointer": {
"pointer": "/nodes/0/rotation"
}
}
},
"sampler": 0
}
],
"samplers": [
{
"input": 0,
"output": 1
}
]
}
]
}
Loading

0 comments on commit 4dcb6a3

Please sign in to comment.