public IStoryLayoutInstance ArrangeAndDecorate(IStoryGraph sg) { var springModel = new NestedCirclesStorySpringModel(coroutineService, x => ArrangeAndDecorateInternal(sg.Root, 0, x, sg), sg); springModel.Apply(); return(new BasicStoryLayoutInstance(sg)); }
private static Dictionary <int, List <int> > BuildNodeExternalConnections(IStoryGraph sg) { var result = new Dictionary <int, List <int> >(); result.GetOrAdd(sg.Leaves.First(), x => new List <int>()); result.GetOrAdd(sg.Leaves.Last(), x => new List <int>()); foreach (var edge in sg.Edges) { var commonParent = sg.GetCommonParent(edge.First, edge.Second); var first = edge.First; while (sg.Parents[first] != commonParent) { var list = result.GetOrAdd(first, x => new List <int>()); list.Add(edge.Second); first = sg.Parents[first]; } var second = edge.Second; while (sg.Parents[second] != commonParent) { var list = result.GetOrAdd(second, x => new List <int>()); list.Add(edge.First); second = sg.Parents[second]; } } return(result); }
private void ArrangeAndDecorateLeaf(int nodeIndex, IStoryGraph sg) { var aStory = sg.Aspects[nodeIndex]; var node = sg.NodeObjects[nodeIndex]; var wallVisuals = CorridorWall(Quaternion.Identity, Vector3.UnitY * FrustumDistance); var frustumVisuals = ModelVisualElement.New() .SetModel(frustumModel) .SetMaterial(frustumMaterial) .SetRenderState(StandardRenderState.New() .SetCullFace(CullFace.Back) .FromGlobalCache()) .SetTransform(new Transform(1, Quaternion.RotationX(-MathHelper.PiOver2), Vector3.Zero)); var visuals = new [] { frustumVisuals, wallVisuals }; var viewpointProps = new TargetedControlledCamera.Props { Target = Vector3.Zero, Distance = FrustumDistance, FieldOfView = MathHelper.PiOver4, ZNear = 0.1f * FrustumDistance, ZFar = 100.0f * FrustumDistance }; var transform2D = new Transform(2, Quaternion.RotationX(-MathHelper.PiOver2), FrustumDistance * Vector3.UnitY); aStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new WallDefaultViewpointMechanismZ(node, viewpointProps), Hittable = GetHittableComponent(node, transform2D), VisualElements = visuals }); }
private void OnStoryGraphChanged() { sg = storyService.GlobalGraph; Reset(sg.NodeIds.Contains(current) ? current : sg.Root); }
public FlowchartTunnelDigger(IStoryGraph graph) { sg = graph; grids = graph.NonLeaves.ToDictionary(x => x, x => new FlowchartGrid()); sizes = new Size2[graph.EnoughIntegers.Count]; positions = new Vector2[graph.EnoughIntegers.Count]; nodeCols = sg.EnoughIntegers.Select(x => - 1).ToArray(); }
public void OnWorldRootChanged(ISceneNode newRoot, IStoryGraph newSg) { gizmosByNodes.Clear(); edgeGizmos.Clear(); Node.ChildNodes.Clear(); CreateGizmosForSubtree(newRoot, 0); OnGraphUpdated(newSg); }
public BuildingStoryLayoutInstance(IInputService inputService, BuildingStoryLayoutPlacementAlgorithm placementAlgorithm, ICollisionMesh collisionMesh, IStoryLayoutZoning zoning) { this.placementAlgorithm = placementAlgorithm; this.collisionMesh = collisionMesh; this.zoning = zoning; this.inputService = inputService; sg = placementAlgorithm.StoryGraph; }
public BuildingLaneBuilder(IStoryGraph sg, Func <int, Transform> getLocalTransform) { this.sg = sg; this.getLocalTransform = getLocalTransform; lanes = new Dictionary <Pair <int>, BuildingLane>(); rawPoints = new List <Vector3>(); rawSegments = new List <BuildingRawLaneSegment>(); rawSegmentsByParentNodes = new Dictionary <int, List <BuildingRawLaneSegment> >(); }
private void OnGraphUpdated(IStoryGraph newSg) { UpdateRouteEdges(newSg); Rearrange(newSg); // digger only flowchartTunnelDigger = new FlowchartTunnelDigger(newSg); ApplyDigger(); AdjustEdgeGizmos(); FixNames(); }
private static IEnumerable <int> EnumerateParentChain(IStoryGraph sg, int node) { var parent = sg.Parents[node]; while (parent != sg.Parents[sg.Root]) { yield return(parent); parent = sg.Parents[parent]; } }
public NestedCirclesStorySpringModel(ICoroutineService coroutineService, Action <NestedCirclesStorySpringModel> visualize, IStoryGraph sg) { Debug.Assert(SpringConstant * IterationDeltaT < 1, "SpringConstant * IterationDeltaT < 1"); this.coroutineService = coroutineService; this.visualize = visualize; this.sg = sg; nodeExternalConnections = BuildNodeExternalConnections(sg); positions = new Vector2[sg.EnoughIntegers.Count]; oldPositions = new Vector2[sg.EnoughIntegers.Count]; radii = new float[sg.EnoughIntegers.Count]; placed = new bool[sg.EnoughIntegers.Count]; }
public BuildingStoryLayoutPlacementAlgorithm(ICoroutineService coroutineService, Action <BuildingStoryLayoutPlacementAlgorithm> visualize, IStoryGraph sg) { this.coroutineService = coroutineService; this.visualize = visualize; this.sg = sg; laneBuilder = new BuildingLaneBuilder(sg, x => relativeTransforms[x]); nodeRows = sg.EnoughIntegers.Select(x => - 1).ToArray(); grids = sg.EnoughIntegers.ToDictionary(x => x, x => new BuildingStoryLayoutAbstractGrid()); relPositions = new Vector3[sg.EnoughIntegers.Count]; halfSizes = new Size3[sg.EnoughIntegers.Count]; isReversed = new bool[sg.EnoughIntegers.Count]; relativeTransforms = new Dictionary <int, Transform>(); }
private void UpdateRouteEdges(IStoryGraph storyGraph) { var allRouteEdges = new HashSet <UnorderedPair <ISceneNode> >(storyGraph.Edges .Select(x => new UnorderedPair <ISceneNode>(storyGraph.NodeObjects[x.First], storyGraph.NodeObjects[x.Second]))); var deathNote = edgeGizmos.Keys.Where(x => x.Second.ParentNode != x.First && !allRouteEdges.Contains(x)).ToList(); foreach (var key in deathNote) { DeleteEdge(key); } foreach (var key in allRouteEdges.Where(x => !edgeGizmos.ContainsKey(x))) { AddNewEdge(key, routeMaterial, routeRenderState); } }
private void ArrangeAndDecorateInternal(IStoryGraph sg, int nodeIndex, int scaleLevel) { var aStory = sg.Aspects[nodeIndex]; var node = sg.NodeObjects[nodeIndex]; var abstractChildren = sg.Children[nodeIndex]; var numChildren = abstractChildren.Count; var scale = MathHelper.Pow(4, scaleLevel); var visuals = new [] { ModelVisualElement.New() .SetModel(planeModel) .SetMaterial(planeMaterials[scaleLevel % planeMaterials.Length]) .SetTransform(Transform.Scaling(10 * scale)) }; var viewpointProps = new FreeControlledCamera.Props { Eye = Vector3.UnitZ * 10f * scale, Yaw = MathHelper.PiOver4, Pitch = MathHelper.PiOver4, FieldOfView = MathHelper.PiOver4, ZNear = 0.1f * scale, ZFar = 100.0f * scale }; for (var i = 0; i < numChildren; i++) { var adaptiveChild = abstractChildren[i]; var angle = MathHelper.TwoPi * i / numChildren - MathHelper.PiOver4; var rotation = Quaternion.RotationZ(angle); var radius = scale * GetBestRadius(numChildren); var offset = new Vector3(radius * MathHelper.Sin(angle), radius * MathHelper.Cos(angle), 0.1f * scale); sg.NodeObjects[adaptiveChild].Transform = new Transform(1, rotation, offset); ArrangeAndDecorateInternal(sg, adaptiveChild, scaleLevel - 1); } aStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new FreeLandDefaultViewpointMechanism(node, viewpointProps, keyboardInputProvider), Hittable = GetHittableComponent(node), VisualElements = visuals }); }
private void ArrangeIntermediateNode(int node, int depthHeight, IStoryGraph storyGraph) { var aNode = storyGraph.Aspects[node]; var gizmo = gizmosByNodes[aNode.Node]; var firstLeafX = gizmosByNodes[storyGraph.Leaves.Select(x => storyGraph.NodeObjects[x]).First(x => x.IsDescendantOf(aNode.Node))].Transform.Offset.X; var lastLeafX = gizmosByNodes[storyGraph.Leaves.Select(x => storyGraph.NodeObjects[x]).Last(x => x.Node.IsDescendantOf(aNode.Node)).Node].Transform.Offset.X; gizmo.Transform = Transform.Translation((firstLeafX + lastLeafX) / 2, OffsetY * depthHeight, 0); if (depthHeight <= 1) { return; } foreach (var child in storyGraph.Children[node].Except(storyGraph.Leaves)) { ArrangeIntermediateNode(child, depthHeight - 1, storyGraph); } }
// public IStoryLayoutInstance ArrangeAndDecorate(IStoryGraph sg) { var placementAlgorithm = new BuildingStoryLayoutPlacementAlgorithm(coroutineService, x => ArrangeAndDecorateInternal(sg.Root, x, new List <BuildingWallSegment>()), sg); placementAlgorithm.Run(); var collisionSegments = new List <BuildingWallSegment>(); ArrangeAndDecorateInternal(sg.Root, placementAlgorithm, collisionSegments); var floors = sg.Children[sg.Root] .Select(x => new AaBox(placementAlgorithm.RelativeTransforms[x].Offset, placementAlgorithm.HalfSizes[x])) .ToArray(); var zonesWithProperties = floors.Select(x => Tuples.Pair(x, new StoryLayoutZoneProperties(-15f))).ToArray(); var defaultZoneProperties = new StoryLayoutZoneProperties(0); var zoning = new AaBoxStoryLayoutZoning(zonesWithProperties, defaultZoneProperties); var buildingCollisionMesh = new CollisionMesh(collisionSegments, floors, zoning); return(new BuildingStoryLayoutInstance(inputService, placementAlgorithm, buildingCollisionMesh, zoning)); }
private void Rearrange(IStoryGraph storyGraph) { var leafGizmos = storyGraph.Leaves.Select(x => gizmosByNodes[storyGraph.NodeObjects[x]]).ToList(); { var width = 0f; foreach (var leafGizmo in leafGizmos) { //leafGizmo.Transform.Own = Transform.Translation(width + OffsetX / 2, 0, 0); var cGizmo = leafGizmo.GetComponent <StoryFlowchartNodeGizmoComponent>(); cGizmo.Depth = storyGraph.Depths[cGizmo.ReferencedNode.Id]; width += OffsetX; } } if (storyGraph.Depth > 1) { ArrangeIntermediateNode(storyGraph.Root, storyGraph.Depth - 1, storyGraph); } }
private void ArrangeAndDecorateInternal(int nodeIndex, int level, NestedCirclesStorySpringModel springModel, IStoryGraph sg) { var node = sg.NodeObjects[nodeIndex]; var aspect = sg.Aspects[nodeIndex]; var index = node.Id; var dynamicParts = new StoryNodeDynamicParts(); var scale = springModel.GetVisualRadius(index); var visualElems = new List <IVisualElement> { new ModelVisualElement <object>(null) .SetModel(circleModel) .SetMaterial(circleMaterials[level % circleMaterials.Length]) .SetRenderState(circleRenderState) .SetTransform(Transform.Scaling(scale)) .SetTransformSpace(TransformSpace.ScreenAlighned) }; var transform = Transform.Translation(new Vector3(springModel.GetPosition(index), 0)); node.Transform = transform; if (level == 0) { var edgeVisuals = sg.Edges.Select(edge => CreateEdgeVisualElement(node, sg.NodeObjects[edge.First], sg.NodeObjects[edge.Second])); visualElems.AddRange(edgeVisuals); } if (sg.Children[index].Any()) { foreach (var childIndex in sg.Children[index]) { ArrangeAndDecorateInternal(childIndex, level + 1, springModel, sg); } } dynamicParts.VisualElements = visualElems; dynamicParts.Hittable = new CircleHittable <ISceneNode>(node, Transform.Identity, x => new Circle2(Vector2.Zero, scale), x => - 0.01f * level); dynamicParts.DefaultViewpointMechanism = new OrthoDefaultViewpointMechanism(node, new PlaneOrthoBoundControlledCamera.Props { Target = Vector2.Zero, Distance = 1.5f * scale, ZNear = 0.1f, ZFar = 1000f }); aspect.SetDynamicParts(dynamicParts); }
public IStoryLayoutInstance ArrangeAndDecorate(IStoryGraph sg) { ArrangeAndDecorateInternal(sg, sg.Root, sg.Depth); return(new BasicStoryLayoutInstance(sg)); }
private void ArrangeAndDecorateRoot(IStoryGraph sg) { var nodeIndex = sg.Root; var aStory = sg.Aspects[nodeIndex]; var node = sg.NodeObjects[nodeIndex]; var adaptiveChildren = sg.Children[nodeIndex]; var numChildren = adaptiveChildren.Count; var visuals = new List <IVisualElement>(); // todo: refactor to CenterLayout struct var wallRadius = 1f; var alpha = MathHelper.PiOver2; if (numChildren > 2) { alpha = MathHelper.TwoPi / (2 * numChildren); wallRadius = corridorHalfWidth / MathHelper.Tan(alpha / 2f); } for (int i = 0; i < numChildren; i++) { var centerAngle = alpha * (1 + 2 * i); var pos = new Vector3( wallRadius * MathHelper.Cos(centerAngle), wallRadius * MathHelper.Sin(centerAngle), 0); var rotation = Quaternion.RotationZ(-centerAngle + MathHelper.PiOver2); visuals.Add(CorridorWall(rotation, pos)); } for (int i = 0; i < numChildren; i++) { var child = adaptiveChildren[i]; var centerAngle = alpha * (2 * i); var doorPos = new Vector3( wallRadius * MathHelper.Cos(centerAngle), wallRadius * MathHelper.Sin(centerAngle), 0); var forward = doorPos.Normalize(); var rotation = Quaternion.RotationZ(-centerAngle + MathHelper.PiOver2); if (sg.Children[child].Any()) { sg.NodeObjects[child].Transform = new Transform(1, rotation, doorPos); ArrangeAndDecorateIntermediate(child, sg); } else { sg.NodeObjects[child].Transform = new Transform(1, rotation, doorPos - forward * FrustumDistance); ArrangeAndDecorateLeaf(child, sg); } } // floor visuals.Add(ModelVisualElement.New() .SetModel(floorModel) .SetMaterial(floorMaterial) .SetRenderState(StandardRenderState.New() .SetCullFace(CullFace.Back) .FromGlobalCache()) .SetTransform(new Transform(1, Quaternion.Identity, new Vector3(0, 0, centerHeight - ceilingHalfHeight)))); // ceiling visuals.Add(ModelVisualElement.New() .SetModel(ceilingModel) .SetMaterial(ceilingMaterial) .SetRenderState(StandardRenderState.New() .SetCullFace(CullFace.Back) .FromGlobalCache()) .SetTransform(new Transform(1, Quaternion.Identity, new Vector3(0, 0, centerHeight + ceilingHalfHeight)))); var viewpointProps = new LookAroundCamera.Props { Distance = 24, FieldOfView = MathHelper.PiOver2, ZNear = 0.1f, ZFar = 100.0f, Pitch = 0 }; aStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new SphereDefaultViewpointMechanism(node, viewpointProps), Hittable = new DummyHittable(), VisualElements = visuals }); }
private void ArrangeAndDecorateInternal(IStoryGraph sg, int nodeId, AaRectangle2 yawPitchBounds, float radius) { var cStory = sg.Aspects[nodeId]; var node = sg.NodeObjects[nodeId]; var children = sg.Children[nodeId]; var numChildren = children.Count; var yaw = yawPitchBounds.Center.X; var pitch = yawPitchBounds.Center.Y; var pos = ToCartesian(yaw, pitch, radius); var zAxis = (-pos).Normalize(); var xAxis = Vector3.Cross(Vector3.UnitY, zAxis).Normalize(); var yAxis = Vector3.Cross(zAxis, xAxis); var rotation = Quaternion.RotationToFrame(xAxis, yAxis); node.Transform = new Transform(1, rotation, pos); var numRows = (int)Math.Ceiling(MathHelper.Sqrt(numChildren)); var numCols = (int)Math.Ceiling((float)numChildren / numRows); var totalHeightRequired = MinVerticalDistance * numRows; var totalWidthRequired = MinHorizontalDistance * numRows; var minChildrenRadius = Math.Max(totalWidthRequired / yawPitchBounds.Width, totalHeightRequired / yawPitchBounds.Height); var childRadius = Math.Max(radius + MinRadiusDistance, minChildrenRadius); for (var i = 0; i < numChildren; i++) { var child = children[i]; var row = i / numCols; var col = i % numCols; var childYawWidth = yawPitchBounds.Width / numCols; var childYawHeight = yawPitchBounds.Height / numRows; var childYaw = yawPitchBounds.MinX + childYawWidth * (row + 0.5f); var childPitch = yawPitchBounds.MinY + childYawHeight * (col * 0.5f); var childYawPitchBounds = AaRectangle2.FromCenter(new Vector2(childYaw, childPitch), HalfWidth / childRadius, HalfHeight / childRadius); ArrangeAndDecorateInternal(sg, child, childYawPitchBounds, childRadius); } var visuals = new [] { ModelVisualElement.New(sg.Aspects[sg.Root]) .SetModel(frustumModel) .SetMaterial(frustumMaterial) .SetTransform(new Transform(0.5f, Quaternion.Identity, new Vector3(0, 0, 0.5f * MathHelper.FrustumDistance))) .SetHide(x => !x.ShowAux1) }; var viewpointProps = new TargetedControlledCameraY.Props { Target = Vector3.Zero, Distance = MathHelper.FrustumDistance, FieldOfView = MathHelper.PiOver4, ZNear = 0.1f, ZFar = 100.0f }; var transform2D = new Transform(2, Quaternion.Identity, -MathHelper.FrustumDistance * Vector3.UnitZ); cStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new WallDefaultViewpointMechanism(node, viewpointProps), Hittable = GetHittableComponent(node, transform2D), VisualElements = visuals, PlacementSurface2D = new PlanarPlacementSurface(node, Transform.Identity), PlacementSurface3D = new PlanarPlacementSurface(node, new Transform(0.05f, Quaternion.Identity, new Vector3(0, 0, 0.5f * MathHelper.FrustumDistance))) }); }
public IStoryLayoutInstance ArrangeAndDecorate(IStoryGraph sg) { ArrangeAndDecorateRoot(sg); return(new BasicStoryLayoutInstance(sg)); }
private static bool IsIndirectParent(IStoryGraph sg, int child, int parent) => EnumerateParentChain(sg, child).Any(x => x == parent);
public void OnEdgeRemoved(Pair <int> edge, IStoryGraph newSg) { OnGraphUpdated(newSg); }
public void OnStoryNodeAdded(ISceneNode parent, ISceneNode newNode, IStoryGraph newSg) { CreateGizmosForSubtree(newNode, newSg.Depths[newNode.Id]); OnGraphUpdated(newSg); }
public IStoryLayoutInstance ArrangeAndDecorate(IStoryGraph sg) { ArrangeAndDecorateLevel(sg, sg.Root, sg.Depth, true); return(new BasicStoryLayoutInstance(sg)); }
public void OnStoryNodeRemoved(ISceneNode parent, ISceneNode removedNode, IStoryGraph newSg) { DeleteGizmoSubtree(removedNode); OnGraphUpdated(newSg); }
private void ArrangeAndDecorateIntermediate(int nodeIndex, IStoryGraph sg) { var aStory = sg.Aspects[nodeIndex]; var node = sg.NodeObjects[nodeIndex]; var abstractChildren = sg.Children[nodeIndex]; var numChildren = abstractChildren.Count; var forward = Vector3.UnitY; var right = Vector3.UnitX; var rightWallPos = forward * exitHalfLength + right * corridorHalfWidth; var rightWallRot = Quaternion.RotationZ(MathHelper.PiOver2); var leftWallPos = forward * exitHalfLength - right * corridorHalfWidth; var leftWallRot = Quaternion.RotationZ(-MathHelper.PiOver2); var visuals = new List <IVisualElement>(); // right exit wall visuals.Add(ExitWall(rightWallRot, rightWallPos)); // left exit wall visuals.Add(ExitWall(leftWallRot, leftWallPos)); var viewpointProps = new TargetedControlledCamera.Props { Target = Vector3.Zero, Distance = FrustumDistance, FieldOfView = MathHelper.PiOver4, ZNear = 0.1f * FrustumDistance, ZFar = 100.0f * FrustumDistance }; var i = 0; //var numSegments = adaptiveLayout.AdaptiveChildren.Count * 2 - 1; var numSegments = numChildren % 2 == 0 ? numChildren + 1 : numChildren; while (i < numSegments) { //var childIndex = i / 2; var childIndex = i; var exitOffset = 2 * exitHalfLength; var neighborOffset = 2 * corridorHalfWidth; Vector3 childForward; Vector3 position; Quaternion rotation; if (i < numSegments / 2) { childForward = right; position = forward * (exitOffset + neighborOffset * i + corridorHalfWidth) + childForward * corridorHalfWidth; rotation = rightWallRot; } else if (i > numSegments / 2) { childForward = -right; var numNeighborsAfter = numSegments - i - 1; position = forward * (exitOffset + neighborOffset * numNeighborsAfter + corridorHalfWidth) + childForward * corridorHalfWidth; rotation = leftWallRot; } else { childForward = forward; position = forward * (exitOffset + neighborOffset * i); rotation = Quaternion.Identity; } if (i < numChildren) { var adaptiveChild = abstractChildren[childIndex]; if (abstractChildren.Any()) { sg.NodeObjects[adaptiveChild].Transform = new Transform(1, rotation, position); ArrangeAndDecorateIntermediate(adaptiveChild, sg); } else { sg.NodeObjects[adaptiveChild].Transform = new Transform(1, rotation, position - childForward * FrustumDistance); ArrangeAndDecorateLeaf(adaptiveChild, sg); } } else { visuals.Add(CorridorWall(rotation, position)); } i++; } aStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new WallDefaultViewpointMechanismZ(node, viewpointProps), Hittable = new DummyHittable(), VisualElements = visuals }); }
private void ArrangeAndDecorateRoot(IStoryGraph sg, int nodeIndex) { var cStory = sg.Aspects[nodeIndex]; var node = sg.NodeObjects[nodeIndex]; var children = sg.Children[nodeIndex]; var numChildren = children.Count; if (numChildren < 2) { var viewpointProps = new TargetedControlledCameraY.Props { Target = Vector3.Zero, Distance = MathHelper.FrustumDistance, FieldOfView = MathHelper.PiOver4, ZNear = 0.1f, ZFar = 100.0f }; cStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new WallDefaultViewpointMechanism(node, viewpointProps), Hittable = new DummyHittable(), VisualElements = new IVisualElement[0] }); foreach (var child in children) { ArrangeAndDecorateRoot(sg, child); } } else { var distr = CalculateDistribution(numChildren); var radius = distr.RelativeRadius; var oneMore = children.SelectMany(x => sg.Children[x]).Any(); for (int i = 0; i < numChildren; i++) { var pitch = distr.Angles[i].Pitch; var yaw = distr.Angles[i].Yaw; if (oneMore) { //var internalRadius = radius / 2; ArrangeAndDecorateRoot(sg, children[i]); //var yaw = yawPitchBounds.Center.X; //var pitch = yawPitchBounds.Center.Y; var pos = ToCartesian(yaw, pitch, radius * 20); var zAxis = (-pos).Normalize(); var xAxis = Vector3.Cross(Vector3.UnitY, zAxis).Normalize(); var yAxis = Vector3.Cross(zAxis, xAxis); var rotation = Quaternion.RotationToFrame(xAxis, yAxis); sg.NodeObjects[children[i]].Transform = new Transform(1, rotation, pos); //ArrangeAndDecorateInternal(sg, children[i], // AaRectangle2.FromCenter(new Vector2(yaw, pitch), HalfWidth / radius, HalfHeight / radius), // radius); } else { ArrangeAndDecorateInternal(sg, children[i], AaRectangle2.FromCenter(new Vector2(yaw, pitch), HalfWidth / radius, HalfHeight / radius), radius); } } var viewpointProps = new LookAroundCamera.Props { Distance = distr.RelativeRadius, FieldOfView = 0.75f * MathHelper.Pi, ZNear = 0.1f, ZFar = 100.0f, Pitch = 0//MathHelper.PiOver2 + 0.1f }; cStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new SphereDefaultViewpointMechanism(node, viewpointProps), Hittable = new DummyHittable(), VisualElements = new IVisualElement[0] }); } }
private void ArrangeAndDecorateLevel(IStoryGraph sg, int nodeIndex, int scaleLevel, bool isRoot) { var aStory = sg.Aspects[nodeIndex]; var node = sg.NodeObjects[nodeIndex]; var abstractChildren = sg.Children[nodeIndex]; var scale = MathHelper.Pow(2, scaleLevel / 4f); var halfSize = 1f; var fov = MathHelper.PiOver4; var distance = halfSize / MathHelper.Tan(fov / 2); var visuals = new List <IVisualElement>(); visuals.Add(ModelVisualElement.New() .SetModel(frustumModel) .SetMaterial(frustumMaterial)); var viewpointProps = isRoot ? new TargetedControlledCameraY.Props { Target = Vector3.Zero, Distance = distance * 10, Pitch = MathHelper.Pi * 0.1f, FieldOfView = fov, ZNear = 0.1f * distance * scale, ZFar = 100.0f * distance * scale } : new TargetedControlledCameraY.Props { Target = Vector3.Zero, Distance = distance, FieldOfView = fov, ZNear = 0.1f * distance * scale, ZFar = 100.0f * distance * scale }; if (isRoot) { // todo: change into additional local transform, or move to a 3D child space node.Transform = new Transform(1, Quaternion.RotationToFrame(Vector3.UnitX, Vector3.UnitZ), Vector3.Zero); } for (int i = 0; i < abstractChildren.Count; i++) { var adaptiveChild = abstractChildren[i]; var childBoundingRadius = 5 * scale; var angle = MathHelper.Pi * 0.33f + 3 * MathHelper.TwoPi / 5 * i; var rotation = Quaternion.RotationY(-angle + MathHelper.Pi); var childScalingCompoensation = 10f; var radius = isRoot ? (childBoundingRadius / 3) * (2 + i) * childScalingCompoensation : (childBoundingRadius / 3) * (2 + i * 0.5f) * childScalingCompoensation * 0.5f; var offset = new Vector3(radius * MathHelper.Sin(angle), 0, radius * MathHelper.Cos(angle)); sg.NodeObjects[adaptiveChild].Transform = new Transform(5, rotation, offset); ArrangeAndDecorateLevel(sg, adaptiveChild, scaleLevel - 1, false); visuals.Add(ModelVisualElement.New() .SetModel(circleModel) .SetMaterial(circleMaterial) .SetTransform(new Transform(radius / childScalingCompoensation, Quaternion.RotationToFrame(Vector3.UnitX, Vector3.UnitZ), Vector3.Zero))); } aStory.SetDynamicParts(new StoryNodeDynamicParts { DefaultViewpointMechanism = new WallDefaultViewpointMechanism(node, viewpointProps), Hittable = GetHittableComponent(node), VisualElements = visuals }); }