Skip to content

Commit efcc591

Browse files
Enable CoordinateSystem support in animation export
- Enable support for shape kind CoordinateSystem. - Add moving CoordinateSystems in test Collision/BouncingBeams.jl. - Update docu. - Improve animation export info in README.md.
1 parent 4a5282b commit efcc591

File tree

5 files changed

+105
-35
lines changed

5 files changed

+105
-35
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
[Modia3D](https://github.com/ModiaSim/Modia3D.jl) is a Julia package that integrates a multibody program and 3D shapes for visualization and collision handling into the [Modia Modeling Language](https://github.com/ModiaSim/ModiaLang.jl). It is then, for example, possible to model the 3D mechanical part of a robot with Modia3D and the electrical motors and gearboxes that are driving the joints with the Modia language. Collision handling with elastic response calculation is performed for shapes that are defined with a contact material and have a convex geometry or are approximated by the convex hull of a concave geometry. For more details, see the [Modia3D Tutorial](https://modiasim.github.io/Modia3D.jl/stable/tutorial/Tutorial.html).
77

8-
The multibody program supports currently tree-structured multibody systems, but does not (yet) support kinematic loops.
8+
Modia3D supports currently tree-structured multibody systems, but does not (yet) support kinematic loops.
99

1010
Example videos:
1111

12-
- [YouBot robots with gripping](https://modiasim.github.io/Modia3D.jl/resources/videos/YouBotsGripping.mp4)
12+
- [YouBot robots gripping a workpiece](https://modiasim.github.io/Modia3D.jl/resources/videos/YouBotsGripping.mp4)
1313
- [Billiard table with 16 balls](https://modiasim.github.io/Modia3D.jl/resources/videos/Billard16Balls.mp4)
14-
- [Mobile with depthmax=8](https://modiasim.github.io/Modia3D.jl/resources/videos/Mobile8.mp4)
14+
- [Mobile with 8 levels](https://modiasim.github.io/Modia3D.jl/resources/videos/Mobile8.mp4)
1515

1616

1717
## Installation
@@ -24,7 +24,7 @@ A standalone Modia3D version is installed with
2424
julia> ]add ModiaLang, Modia3D
2525
```
2626

27-
Modia3D animation can be exported in threejs-json-format and then imported in the open source web app [threejs.org](https://threejs.org/editor/) and use all the features of threejs, for example to export in the widely used glb format (= the binary version of the [glTF](https://www.khronos.org/gltf/) format) and use any glb-viewer (for example 3D-Viewer of Windows 10).
27+
Modia3D animation can be exported in [three.js JSON Object Scene format](https://github.com/mrdoob/three.js/wiki/JSON-Object-Scene-format-4) and then imported in the open source web app [three.js editor](https://threejs.org/editor/) and use all the features of three.js, for example to export in the widely used glb format (the binary version of the [glTF](https://www.khronos.org/gltf/) format) and use any glb viewer (for example 3D-Viewer of Windows 10).
2828

2929
Additionally, the (free) community or the (commercial) professional version of the [DLR Visualization](http://www.systemcontrolinnovationlab.de/the-dlr-visualization-library/) library is supported that provides online animation (during simulation) and generation of mpg4-videos. To install the free version for *Windows* or for *Linux* perform the following steps:
3030

src/AnimationExport/exportAnimation.jl

Lines changed: 97 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,57 @@ function name2uuid(name::String)
44
return string(UUIDs.uuid5(u4, name))
55
end
66

7+
const coSysMaterialRed = (; name="coordinateSystem.red", uuid=name2uuid("coordinateSystem.red"), type="MeshPhongMaterial", color=((255*256 + 0)*256 + 0), opacity=1, transparent=false, shininess=0.5)
8+
const coSysMaterialGreen = (; name="coordinateSystem.green", uuid=name2uuid("coordinateSystem.green"), type="MeshPhongMaterial", color=((255*0 + 255)*256 + 0), opacity=1, transparent=false, shininess=0.5)
9+
const coSysMaterialBlue = (; name="coordinateSystem.blue", uuid=name2uuid("coordinateSystem.blue"), type="MeshPhongMaterial", color=((255*0 + 0)*256 + 255), opacity=1, transparent=false, shininess=0.5)
10+
711

812
function exportObject(object, elements, obj::Modia3D.Composition.Object3D, sphere::Modia3D.Shapes.Sphere, initPos, initRot)
913
r_obj = Modia3D.ZeroVector3D
1014
R_obj = Modia3D.NullRotation
11-
name = String(Modia3D.fullName(obj)) * ".geometry"
12-
geometry = (; name=name, uuid=name2uuid(name), type="SphereBufferGeometry", radius=sphere.diameter/2, heightSegments=16, widthSegments=32)
15+
name = Modia3D.fullName(obj)
16+
geometryName = name * ".geometry"
17+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="SphereBufferGeometry", radius=sphere.diameter/2, heightSegments=16, widthSegments=32)
1318
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
14-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot)
19+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot)
1520
printInfoToFile(object, elements, geometry, material, nothing, objectInfo)
1621
return (r_obj, R_obj)
1722
end
1823

1924
function exportObject(object, elements, obj::Modia3D.Composition.Object3D, ellipsoid::Modia3D.Shapes.Ellipsoid, initPos, initRot)
2025
r_obj = Modia3D.ZeroVector3D
2126
R_obj = Modia3D.NullRotation
22-
name = String(Modia3D.fullName(obj)) * ".geometry"
23-
geometry = (; name=name, uuid=name2uuid(name), type="SphereBufferGeometry", radius=ellipsoid.lengthX/2, heightSegments=16, widthSegments=32)
27+
name = Modia3D.fullName(obj)
28+
geometryName = name * ".geometry"
29+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="SphereBufferGeometry", radius=ellipsoid.lengthX/2, heightSegments=16, widthSegments=32)
2430
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
25-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot, scale=[1.0, ellipsoid.lengthY/ellipsoid.lengthX, ellipsoid.lengthZ/ellipsoid.lengthX])
31+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot, scale=[1.0, ellipsoid.lengthY/ellipsoid.lengthX, ellipsoid.lengthZ/ellipsoid.lengthX])
2632
printInfoToFile(object, elements, geometry, material, nothing, objectInfo)
2733
return (r_obj, R_obj)
2834
end
2935

3036
function exportObject(object, elements, obj::Modia3D.Composition.Object3D, box::Modia3D.Shapes.Box, initPos, initRot)
3137
r_obj = Modia3D.ZeroVector3D
3238
R_obj = Modia3D.NullRotation
33-
name = String(Modia3D.fullName(obj)) * ".geometry"
34-
geometry = (; name=name, uuid=name2uuid(name), type="BoxBufferGeometry", width=box.lengthX, height=box.lengthY, depth=box.lengthZ)
39+
name = Modia3D.fullName(obj)
40+
geometryName = name * ".geometry"
41+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="BoxBufferGeometry", width=box.lengthX, height=box.lengthY, depth=box.lengthZ)
3542
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
36-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot)
43+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot)
3744
printInfoToFile(object, elements, geometry, material, nothing, objectInfo)
3845
return (r_obj, R_obj)
3946
end
4047

4148
function exportObject(object, elements, obj::Modia3D.Composition.Object3D, cylinder::Modia3D.Shapes.Cylinder, initPos, initRot)
42-
name = String(Modia3D.fullName(obj)) * ".geometry"
49+
name = Modia3D.fullName(obj)
50+
geometryName = name * ".geometry"
4351
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
4452
if cylinder.innerDiameter == 0.0
4553
r_obj = Modia3D.ZeroVector3D
4654
R_obj = Shapes.rotateAxis2y(cylinder.axis, Modia3D.NullRotation)
4755
shape = nothing
48-
geometry = (; name=name, uuid=name2uuid(name), type="CylinderBufferGeometry", radiusBottom=cylinder.diameter/2, radiusTop=cylinder.diameter/2, height=cylinder.length, radialSegments=32, heightSegments=1)
49-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot, R_obj=R_obj)
56+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="CylinderBufferGeometry", radiusBottom=cylinder.diameter/2, radiusTop=cylinder.diameter/2, height=cylinder.length, radialSegments=32, heightSegments=1)
57+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot, R_obj=R_obj)
5058
else
5159
if cylinder.axis == 1
5260
r_obj = @SVector[-cylinder.length/2, 0.0, 0.0]
@@ -63,8 +71,8 @@ function exportObject(object, elements, obj::Modia3D.Composition.Object3D, cylin
6371
shapeUuid = name2uuid(shapeName)
6472
shape = (; name=shapeName, uuid=shapeUuid, type="Shape", curves=curves, holes=holes, currentPoint=[0, 0])
6573
options = (; depth=cylinder.length, bevelEnabled=false)
66-
geometry = (; name=name, uuid=name2uuid(name), type="ExtrudeGeometry", shapes=[shapeUuid], options=options)
67-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot, r_obj=r_obj, R_obj=R_obj)
74+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="ExtrudeGeometry", shapes=[shapeUuid], options=options)
75+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot, r_obj=r_obj, R_obj=R_obj)
6876
end
6977
printInfoToFile(object, elements, geometry, material, shape, objectInfo)
7078
return (r_obj, R_obj)
@@ -79,18 +87,20 @@ function exportObject(object, elements, obj::Modia3D.Composition.Object3D, cone:
7987
r_obj = @SVector[0.0, 0.0, cone.length/2]
8088
end
8189
R_obj = Shapes.rotateAxis2y(cone.axis, Modia3D.NullRotation)
82-
name = String(Modia3D.fullName(obj)) * ".geometry"
83-
geometry = (; name=name, uuid=name2uuid(name), type="CylinderBufferGeometry", radiusBottom=cone.diameter/2, radiusTop=cone.topDiameter/2, height=cone.length, radialSegments=32, heightSegments=1)
90+
name = Modia3D.fullName(obj)
91+
geometryName = name * ".geometry"
92+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="CylinderBufferGeometry", radiusBottom=cone.diameter/2, radiusTop=cone.topDiameter/2, height=cone.length, radialSegments=32, heightSegments=1)
8493
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
85-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot, r_obj=r_obj, R_obj=R_obj)
94+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot, r_obj=r_obj, R_obj=R_obj)
8695
printInfoToFile(object, elements, geometry, material, nothing, objectInfo)
8796
return (r_obj, R_obj)
8897
end
8998

9099
function exportObject(object, elements, obj::Modia3D.Composition.Object3D, capsule::Modia3D.Shapes.Capsule, initPos, initRot)
91100
r_obj = Modia3D.ZeroVector3D
92101
R_obj = Shapes.rotateAxis2y(capsule.axis, Modia3D.NullRotation)
93-
name = String(Modia3D.fullName(obj)) * ".geometry"
102+
name = Modia3D.fullName(obj)
103+
geometryName = name * ".geometry"
94104
points = []
95105
for i in -9:0
96106
angle = i/9 * pi/2
@@ -102,9 +112,9 @@ function exportObject(object, elements, obj::Modia3D.Composition.Object3D, capsu
102112
point = (; x=capsule.diameter/2*cos(angle), y=capsule.length/2+capsule.diameter/2*sin(angle))
103113
push!(points, point)
104114
end
105-
geometry = (; name=name, uuid=name2uuid(name), type="LatheGeometry", points=points, phiStart=0, phiLength=2*pi, segments=32)
115+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="LatheGeometry", points=points, phiStart=0, phiLength=2*pi, segments=32)
106116
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
107-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot, R_obj=R_obj)
117+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot, R_obj=R_obj)
108118
printInfoToFile(object, elements, geometry, material, nothing, objectInfo)
109119
return (r_obj, R_obj)
110120
end
@@ -118,27 +128,80 @@ function exportObject(object, elements, obj::Modia3D.Composition.Object3D, beam:
118128
r_obj = @SVector[0.0, -beam.thickness/2, 0.0]
119129
end
120130
R_obj = Shapes.rotateAxis2x(beam.axis, Modia3D.NullRotation)
121-
name = String(Modia3D.fullName(obj)) * ".geometry"
131+
name = Modia3D.fullName(obj)
132+
geometryName = name * ".geometry"
122133
curves = [
123134
(; type="LineCurve", v1=[-beam.length/2, -beam.width/2], v2=[beam.length/2, -beam.width/2]),
124135
(; type="EllipseCurve", aX=beam.length/2, aY=0, xRadius=beam.width/2, yRadius=beam.width/2, aStartAngle=-pi/2, aEndAngle=pi/2, aClockwise=false, aRotation=0),
125136
(; type="LineCurve", v1=[beam.length/2, beam.width/2], v2=[-beam.length/2, beam.width/2]),
126137
(; type="EllipseCurve", aX=-beam.length/2, aY=0, xRadius=beam.width/2, yRadius=beam.width/2, aStartAngle=pi/2, aEndAngle=-pi/2, aClockwise=false, aRotation=0)
127138
]
128-
shapeName = String(Modia3D.fullName(obj)) * ".shape"
139+
shapeName = name * ".shape"
129140
shapeUuid = name2uuid(shapeName)
130141
shape = (; name=shapeName, uuid=shapeUuid, type="Shape", curves=curves, holes=[], currentPoint=[0, 0])
131142
options = (; depth=beam.thickness, bevelEnabled=false)
132-
geometry = (; name=name, uuid=name2uuid(name), type="ExtrudeGeometry", shapes=[shapeUuid], options=options)
143+
geometry = (; name=geometryName, uuid=name2uuid(geometryName), type="ExtrudeGeometry", shapes=[shapeUuid], options=options)
133144
material = printVisuMaterialToJSON(obj, obj.visualMaterial)
134-
objectInfo = getObjectInfo(obj, geometry, material, initPos, initRot, r_obj=r_obj, R_obj=R_obj)
145+
objectInfo = getObjectInfo(name, geometry, material, initPos, initRot, r_obj=r_obj, R_obj=R_obj)
135146
printInfoToFile(object, elements, geometry, material, shape, objectInfo)
136147
return (r_obj, R_obj)
137148
end
138149

150+
function exportObject(object, elements, obj::Modia3D.Composition.Object3D, coordinateSystem::Modia3D.Shapes.CoordinateSystem, initPos, initRot)
151+
r_obj = Modia3D.ZeroVector3D
152+
R_obj = Modia3D.NullRotation
153+
name = Modia3D.fullName(obj)
154+
group = (; name=name, uuid=name2uuid(name), type="Group", children=[], position=initPos, rotation=Modia3D.rot123fromR(initRot), scale=ones(3))
155+
156+
geometryName = name * ".axis.geometry"
157+
geometryUuid = name2uuid(geometryName)
158+
geometry = (; name=geometryName, uuid=geometryUuid, type="CylinderBufferGeometry", radiusBottom=coordinateSystem.length/50, radiusTop=coordinateSystem.length/50, height=coordinateSystem.length, radialSegments=8, heightSegments=1)
159+
push!(elements.geometries, geometry)
160+
161+
r_geo = @SVector[coordinateSystem.length/2, 0.0, 0.0]
162+
R_geo = Shapes.rotateAxis2y(1, Modia3D.NullRotation)
163+
child = (; type="Mesh", geometry=geometryUuid, material=coSysMaterialRed.uuid, position=r_geo, rotation=Modia3D.rot123fromR(R_geo), scale=ones(3))
164+
push!(group.children, child)
165+
166+
r_geo = @SVector[0.0, coordinateSystem.length/2, 0.0]
167+
R_geo = Shapes.rotateAxis2y(2, Modia3D.NullRotation)
168+
child = (; type="Mesh", geometry=geometryUuid, material=coSysMaterialGreen.uuid, position=r_geo, rotation=Modia3D.rot123fromR(R_geo), scale=ones(3))
169+
push!(group.children, child)
170+
171+
r_geo = @SVector[0.0, 0.0, coordinateSystem.length/2]
172+
R_geo = Shapes.rotateAxis2y(3, Modia3D.NullRotation)
173+
child = (; type="Mesh", geometry=geometryUuid, material=coSysMaterialBlue.uuid, position=r_geo, rotation=Modia3D.rot123fromR(R_geo), scale=ones(3))
174+
push!(group.children, child)
175+
176+
geometryName = name * ".tip.geometry"
177+
geometryUuid = name2uuid(geometryName)
178+
geometry = (; name=geometryName, uuid=geometryUuid, type="CylinderBufferGeometry", radiusBottom=coordinateSystem.length/25, radiusTop=0.0, height=coordinateSystem.length/10, radialSegments=8, heightSegments=1)
179+
push!(elements.geometries, geometry)
180+
181+
r_geo = @SVector[1.05*coordinateSystem.length, 0.0, 0.0]
182+
R_geo = Shapes.rotateAxis2y(1, Modia3D.NullRotation)
183+
child = (; type="Mesh", geometry=geometryUuid, material=coSysMaterialRed.uuid, position=r_geo, rotation=Modia3D.rot123fromR(R_geo), scale=ones(3))
184+
push!(group.children, child)
185+
186+
r_geo = @SVector[0.0, 1.05*coordinateSystem.length, 0.0]
187+
R_geo = Shapes.rotateAxis2y(2, Modia3D.NullRotation)
188+
child = (; type="Mesh", geometry=geometryUuid, material=coSysMaterialGreen.uuid, position=r_geo, rotation=Modia3D.rot123fromR(R_geo), scale=ones(3))
189+
push!(group.children, child)
190+
191+
r_geo = @SVector[0.0, 0.0, 1.05*coordinateSystem.length]
192+
R_geo = Shapes.rotateAxis2y(3, Modia3D.NullRotation)
193+
child = (; type="Mesh", geometry=geometryUuid, material=coSysMaterialBlue.uuid, position=r_geo, rotation=Modia3D.rot123fromR(R_geo), scale=ones(3))
194+
push!(group.children, child)
195+
196+
push!(object.children, group)
197+
return (r_obj, R_obj)
198+
end
199+
139200
function printInfoToFile(object, elements, geometry, material, shape, objectInfo)
140201
push!(elements.geometries, geometry)
141-
push!(elements.materials, material)
202+
if !isnothing(material)
203+
push!(elements.materials, material)
204+
end
142205
if !isnothing(shape)
143206
push!(elements.shapes, shape)
144207
end
@@ -186,8 +249,7 @@ function getObjectInfoMesh(obj, initPos, initRot, scale, R_obj, meshInfo)
186249
return meshInfo
187250
end
188251

189-
function getObjectInfo(obj, geometry, material, initPos, initRot; r_obj=Modia3D.ZeroVector3D, R_obj=Modia3D.NullRotation, scale=ones(3))
190-
name = String(Modia3D.fullName(obj))
252+
function getObjectInfo(name, geometry, material, initPos, initRot; r_obj=Modia3D.ZeroVector3D, R_obj=Modia3D.NullRotation, scale=ones(3))
191253
return (; name=name, uuid=name2uuid(name), type=:Mesh, geometry=get(geometry, :uuid, nothing), material=get(material, :uuid, ""), position=initPos+initRot'*r_obj, rotation=Modia3D.rot123fromR(R_obj*initRot), scale=scale)
192254
end
193255

@@ -236,6 +298,10 @@ function printObjectToJSON(object, elements, obj; initPos=obj.r_abs, initRot=obj
236298
beam::Modia3D.Shapes.Beam = obj.shape
237299
(r_obj, R_obj) = exportObject(object, elements, obj, beam, initPos, initRot)
238300

301+
elseif shapeKind == Modia3D.CoordinateSystemKind
302+
coordinateSystem::Modia3D.Shapes.CoordinateSystem = obj.shape
303+
(r_obj, R_obj) = exportObject(object, elements, obj, coordinateSystem, initPos, initRot)
304+
239305
elseif shapeKind == Modia3D.FileMeshKind
240306
fileMesh::Modia3D.Shapes.FileMesh = obj.shape
241307
(r_obj, R_obj) = exportObject(object, elements, obj, fileMesh, initPos, initRot)
@@ -334,6 +400,10 @@ function exportAnimation(scene)
334400
(r_Camera, R_Camera) = Modia3D.cameraPosition(options.cameraDistance, options.cameraLongitude, options.cameraLatitude, cameraUpDir, sceneUpDir)
335401
addCameras!(object, uuid, r_Camera, R_Camera)
336402

403+
push!(elements.materials, coSysMaterialRed)
404+
push!(elements.materials, coSysMaterialGreen)
405+
push!(elements.materials, coSysMaterialBlue)
406+
337407
if !isnothing(animation) && length(animation) != 0
338408
iobj = 0
339409
tracks = []

src/Composition/object3D.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ mutable struct Object3D <: Modia3D.AbstractObject3D
115115
hasChildJoint::Bool # = true if its child has a joint
116116
computeAcceleration::Bool # = true if acceleration needs to be computed
117117

118-
# internal shortcuts to avoid costly dynamic multiple dispatch
118+
# internal shortcuts to avoid costly runtime dispatch
119119
shapeKind::Shapes.ShapeKind # marks the defined shape
120120
shape::Modia3D.AbstractShape # stores shape defined in Solid or Visual
121121
visualMaterial::Shapes.VisualMaterial # stores visualMaterial defined in Solid or Visual

src/Shapes/shape.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ The reference frame = Object3D frame coincides with the coordinate system.
66
77
# Arguments
88
- `length` defines the length of the coordinate system axes.
9-
10-
# Notes
11-
- CoordinateSystem is [not supported by animation export](https://github.com/ModiaSim/PrivateModia3D.jl/issues/77).
129
"""
1310
mutable struct CoordinateSystem <: Modia3D.AbstractShape
1411
length::Float64

0 commit comments

Comments
 (0)