Skip to content

Commit

Permalink
Add support for VRMC_node_constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
mrxz committed Nov 7, 2024
1 parent 663c61b commit caa0664
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

* Added support for `VRMC_materials_mtoon` extension.

* Added support for `VRMC_node_constraint` extension.

## 2.0.0-dev.3.10

### New Features
Expand Down
1 change: 1 addition & 0 deletions lib/src/base/gltf.dart
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ class Gltf extends GltfProperty {
gltf.nodes.forEachWithIndices((i, node) {
if (!node.isJoint &&
!node.isCollider &&
!node.isConstraintSource &&
node.children == null &&
node.mesh == null &&
node.camera == null &&
Expand Down
1 change: 1 addition & 0 deletions lib/src/base/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Node extends GltfChildOfRootProperty {

bool isJoint = false;
bool isCollider = false;
bool isConstraintSource = false;

Node._(
this._cameraIndex,
Expand Down
229 changes: 229 additions & 0 deletions lib/src/ext/VRMC_node_constraint/vrmc_constraint.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
/*
* # Copyright (c) 2024 Noeri Huisman
* #
* # Licensed under the Apache License, Version 2.0 (the "License");
* # you may not use this file except in compliance with the License.
* # You may obtain a copy of the License at
* #
* # http://www.apache.org/licenses/LICENSE-2.0
* #
* # Unless required by applicable law or agreed to in writing, software
* # distributed under the License is distributed on an "AS IS" BASIS,
* # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* # See the License for the specific language governing permissions and
* # limitations under the License.
*/

library gltf.extensions.vrmc_vrm;

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

// https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_node_constraint-1.0/schema/VRMC_node_constraint.constraint.schema.json
const String ROLL = 'roll';
const String AIM = 'aim';
const String ROTATION = 'rotation';

const List<String> CONSTRAINT_MEMBERS = <String>[
ROLL,
AIM,
ROTATION,
];

class Constraint extends GltfProperty {
final RollConstraint roll;
final AimConstraint aim;
final RotationConstraint rotation;

Constraint._(this.roll, this.aim, this.rotation,
Map<String, Object> extensions, Object extras)
: super(extensions, extras);

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

var total = (map.containsKey(ROLL) ? 1 : 0) +
(map.containsKey(AIM) ? 1 : 0) +
(map.containsKey(ROTATION) ? 1 : 0);
if (context.validate && total != 1) {
context.addIssue(SchemaError.oneOfMismatch, args: CONSTRAINT_MEMBERS);
}

return Constraint._(
getObjectFromInnerMap(map, ROLL, context, RollConstraint.fromMap),
getObjectFromInnerMap(map, AIM, context, AimConstraint.fromMap),
getObjectFromInnerMap(
map, ROTATION, context, RotationConstraint.fromMap),
getExtensions(map, Constraint, context),
getExtras(map, context));
}

@override
void link(Gltf gltf, Context context) {
if (roll != null) {
context.path.add(ROLL);
roll.link(gltf, context);
context.path.removeLast();
}

if (aim != null) {
context.path.add(AIM);
aim.link(gltf, context);
context.path.removeLast();
}

if (rotation != null) {
context.path.add(ROTATION);
rotation.link(gltf, context);
context.path.removeLast();
}
}
}

// https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_node_constraint-1.0/schema/VRMC_node_constraint.rollConstraint.schema.json
const String SOURCE = 'source';
const String ROLL_AXIS = 'rollAxis';
const String WEIGHT = 'weight';
const List<String> ROLL_CONSTRAINT_MEMBERS = <String>[
SOURCE,
ROLL_AXIS,
WEIGHT,
];

class RollConstraint extends GltfProperty {
final int source;
final String rollAxis;
final double weight;

RollConstraint._(this.source, this.rollAxis, this.weight,
Map<String, Object> extensions, Object extras)
: super(extensions, extras);

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

return RollConstraint._(
getIndex(map, SOURCE, context),
getString(map, ROLL_AXIS, context, req: true, list: ['X', 'Y', 'Z']),
getFloat(map, WEIGHT, context, min: 0.0, max: 1.0, def: 1.0),
getExtensions(map, RollConstraint, context),
getExtras(map, context));
}

@override
void link(Gltf gltf, Context context) {
var node = gltf.nodes[source];

if (context.validate && source != -1) {
if (node == null) {
context.addIssue(LinkError.unresolvedReference,
name: SOURCE, args: [source]);
} else {
node
..markAsUsed()
..isConstraintSource = true;
}
}
}
}

// https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_node_constraint-1.0/schema/VRMC_node_constraint.aimConstraint.schema.json
const String AIM_AXIS = 'aimAxis';
const List<String> AIM_CONSTRAINT_MEMBERS = <String>[
SOURCE,
AIM_AXIS,
WEIGHT,
];

class AimConstraint extends GltfProperty {
final int source;
final String aimAxis;
final double weight;

AimConstraint._(this.source, this.aimAxis, this.weight,
Map<String, Object> extensions, Object extras)
: super(extensions, extras);

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

return AimConstraint._(
getIndex(map, SOURCE, context),
getString(map, AIM_AXIS, context, req: true, list: [
'PositiveX',
'NegativeX',
'PositiveY',
'NegativeY',
'PositiveZ',
'NegativeZ'
]),
getFloat(map, WEIGHT, context, min: 0.0, max: 1.0, def: 1.0),
getExtensions(map, AimConstraint, context),
getExtras(map, context));
}

@override
void link(Gltf gltf, Context context) {
var node = gltf.nodes[source];

if (context.validate && source != -1) {
if (node == null) {
context.addIssue(LinkError.unresolvedReference,
name: SOURCE, args: [source]);
} else {
node
..markAsUsed()
..isConstraintSource = true;
}
}
}
}

// https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_node_constraint-1.0/schema/VRMC_node_constraint.rotationConstraint.schema.json
const List<String> ROTATION_CONSTRAINT_MEMBERS = <String>[
SOURCE,
WEIGHT,
];

class RotationConstraint extends GltfProperty {
final int source;
final double weight;

RotationConstraint._(
this.source, this.weight, Map<String, Object> extensions, Object extras)
: super(extensions, extras);

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

return RotationConstraint._(
getIndex(map, SOURCE, context),
getFloat(map, WEIGHT, context, min: 0.0, max: 1.0, def: 1.0),
getExtensions(map, RotationConstraint, context),
getExtras(map, context));
}

@override
void link(Gltf gltf, Context context) {
var node = gltf.nodes[source];

if (context.validate && source != -1) {
if (node == null) {
context.addIssue(LinkError.unresolvedReference,
name: SOURCE, args: [source]);
} else {
node
..markAsUsed()
..isConstraintSource = true;
}
}
}
}
70 changes: 70 additions & 0 deletions lib/src/ext/VRMC_node_constraint/vrmc_node_constraint.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* # Copyright (c) 2024 Noeri Huisman
* #
* # Licensed under the Apache License, Version 2.0 (the "License");
* # you may not use this file except in compliance with the License.
* # You may obtain a copy of the License at
* #
* # http://www.apache.org/licenses/LICENSE-2.0
* #
* # Unless required by applicable law or agreed to in writing, software
* # distributed under the License is distributed on an "AS IS" BASIS,
* # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* # See the License for the specific language governing permissions and
* # limitations under the License.
*/

library gltf.extensions.vrmc_vrm;

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

// https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_node_constraint-1.0/schema/VRMC_node_constraint.schema.json
const String VRMC_NODE_CONSTRAINT = 'VRMC_node_constraint';
const String SPEC_VERSION = 'specVersion';
const String CONSTRAINT = 'constraint';

const List<String> VRMC_NODE_CONSTRAINT_MEMBERS = <String>[
SPEC_VERSION,
CONSTRAINT,
];

class VrmcNodeConstraint extends GltfProperty {
final String specVersion;
final Constraint constraint;

VrmcNodeConstraint._(this.specVersion, this.constraint,
Map<String, Object> extensions, Object extras)
: super(extensions, extras);

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

final specVersion = getString(map, SPEC_VERSION, context,
req: true, regexp: RegExp(r'^1\.0$'));

return VrmcNodeConstraint._(
specVersion,
getObjectFromInnerMap(map, CONSTRAINT, context, Constraint.fromMap,
req: true),
getExtensions(map, VrmcNodeConstraint, context),
getExtras(map, context));
}

@override
void link(Gltf gltf, Context context) {
if (constraint != null) {
context.path.add(CONSTRAINT);
constraint.link(gltf, context);
context.path.removeLast();
}
}
}

const Extension vrmcNodeConstraint =
Extension(VRMC_NODE_CONSTRAINT, <Type, ExtensionDescriptor>{
Node: ExtensionDescriptor(VrmcNodeConstraint.fromMap),
});
2 changes: 2 additions & 0 deletions lib/src/ext/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import 'package:gltf/src/ext/KHR_materials_volume/khr_materials_volume.dart';
import 'package:gltf/src/ext/KHR_mesh_quantization/khr_mesh_quantization.dart';
import 'package:gltf/src/ext/KHR_texture_transform/khr_texture_transform.dart';
import 'package:gltf/src/ext/VRMC_materials_mtoon/vrmc_materials_mtoon.dart';
import 'package:gltf/src/ext/VRMC_node_constraint/vrmc_node_constraint.dart';
import 'package:gltf/src/ext/VRMC_springBone/vrmc_spring_bone.dart';
import 'package:gltf/src/ext/VRMC_vrm/vrmc_vrm.dart';
import 'package:gltf/src/hash.dart';
Expand Down Expand Up @@ -130,6 +131,7 @@ const List<Extension> kDefaultExtensions = <Extension>[
khrMeshQuantizationExtension,
khrTextureTransformExtension,
vrmcMaterialMtoon,
vrmcNodeConstraint,
vrmcSpringBone,
vrmcVrm,
];

0 comments on commit caa0664

Please sign in to comment.