Needs["OpenCascadeLink`"];
SeedRandom[1];
reflect[vector_,
normal_] = -(vector - 2 (vector - Projection[vector, normal])) //
Simplify;
shape = OpenCascadeShape[
RegionUnion @@
Table[Ball[{Cos[i], Sin[i], 0}, .4], {i, 0, 2 Pi, Pi/6.}]];
bm = OpenCascadeShapeSurfaceMeshToBoundaryMesh[shape,
"ShapeSurfaceMeshOptions" -> {"AngularDeflection" -> .1}];
R = BoundaryMeshRegion[bm];
R2 = RegionBoundary[R];
dist = RegionDistance[R2];
proj = RegionNearest[R2];
pt0 = RandomPoint[R, 1][[1]];
v0 = {1., 1., 0.};
d0 = 0.01*Norm[v0];
sol = NDSolveValue[{r''[t] == {0, 0, 0}, r[0] == pt0, r'[0] == v0,
WhenEvent[dist@r[t] <= d0,
r'[t] -> reflect[r'[t], r[t] - proj@r[t]]]}, r, {t, 0, 100},
MaxStepSize -> 0.01];
ani = Animate[
Show[Graphics3D[{{FaceForm[Opacity[.2], Darker@Cyan], EdgeForm[],
R2}}, Boxed -> False],
ParametricPlot3D[sol@t, {t, 0, c}, Mesh -> {{c}},
MeshStyle -> {AbsolutePointSize[8], Red},
Method -> {"BoundaryOffset" -> False},
PlotStyle -> {Opacity[.9], White}, PlotPoints -> 400,
PerformanceGoal -> "Quality", PlotRange -> All] /. Line -> Arrow,
ViewPoint -> {1, 1, .8}, Background -> Gray], {c, $MachineEpsilon,
100}, AnimationRate -> 1, ControlPlacement -> Bottom]

- Test another type of bounce and another obstacle surfaces.
SeedRandom[1];
reflect[vector_,
normal_] = -(vector - 2 (vector - Projection[vector, normal])) //
Simplify;
surf = ExampleData[{"Geometry3D", "UtahTeapot"}, "Region"];
cube = TransformedRegion[Cuboid @@ Transpose@RegionBounds[surf],
ScalingTransform[{1.2, 1.2, 1.2}, RegionCentroid[surf]]] //
DiscretizeRegion // RegionBoundary;
reg = RegionUnion[surf, cube];
dist = RegionDistance[reg];
proj = RegionNearest[reg];
pt0 = RegionCentroid[reg];
v0 = {1., 1., 0.2};
d0 = 0.01*Norm[v0];
sol = NDSolveValue[{r''[t] == {0, 0, -9.8}, r[0] == pt0, r'[0] == v0,
WhenEvent[dist@r[t] <= d0,
r'[t] -> reflect[r'[t], r[t] - proj@r[t]]]}, r, {t, 0, 20},
MaxStepSize -> 0.001]
ani = Animate[
Show[Graphics3D[{{FaceForm[Opacity[.2], Cyan], EdgeForm[],
surf}, {FaceForm[Opacity[.3], LightGray], EdgeForm[], cube}},
Boxed -> False],
ParametricPlot3D[sol@t, {t, 0, c}, Mesh -> {{c}},
MeshStyle -> {AbsolutePointSize[8], Red},
Method -> {"BoundaryOffset" -> False},
PlotStyle -> {Opacity[.9], White}, PlotPoints -> 400,
PerformanceGoal -> "Quality", PlotRange -> All] /. Line -> Arrow,
Background -> LightGray], {c, $MachineEpsilon, 20},
AnimationRate -> 1, ControlPlacement -> Bottom]

imp? – Spizhen Dec 18 '13 at 21:31imp = x0 + t v0wheretis fromNSolve. So it is nextimpact position. – Kuba Dec 18 '13 at 21:33imp = x0 + t v0I do not know about this. Maybe it's a feature version 9? – Spizhen Dec 18 '13 at 21:44tandv0. what is it? – Spizhen Dec 19 '13 at 06:22tis the parameter, time if you want, x0 and v0 are vectors, of position and velocity.imp = x0 + t v0means that starting fromx0with velocityv0the ball will hit the border afterttime. – Kuba Dec 19 '13 at 06:57gocallsbouncewhich in turns callsgoagain, etc. I've modified my n-ball code to remove thegofunction and simply have a singleWhileloop encompassing thegocode. I wondered if there are any reasons I'm missing which make your approach more favourable? – Quantum_Oli May 12 '16 at 17:03go[]definition explicitly insidebounce? That's perfectly fine. I just like to keep code modularized so ifgo[]happens to be longer it will be more readable to keep it outside. You've motivated me to update this question, I have an idea how to slightly improve performance. – Kuba May 12 '16 at 17:54go[]code and thebounce[]code. I've uploaded a screen cap if you'd like to see the modification for n balls. I've also used aRegionDistancefunction (df), I presume that's likely to factionally improve performance? Screen cap: http://imgur.com/Rjil0gv – Quantum_Oli May 12 '16 at 18:02Floor[currentDistance/dx]steps without that. Even though that closes distance is probably in different direction thanv, it tells us that there is nothing close anyway. – Kuba May 12 '16 at 20:45RegionPlotreturns only the empty rectangular frame, with the ball bouncing off invisible walls. Replacing that line withShow[R2, Graphics[Dynamic@{Disk[x, .1]}]]makes it work again (apart for some styling differences) – glS Aug 27 '16 at 12:06