1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
|
{
Copyright 2003-2018 Michalis Kamburelis.
This file is part of "view3dscene".
"view3dscene" is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"view3dscene" is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with "view3dscene"; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
----------------------------------------------------------------------------
}
{ Scene transformations (available through Edit menu). }
unit V3DSceneSceneChanges;
interface
uses X3DNodes, CastleUtils, CastleScene;
type
{ When adding new item to TSceneChange you can also consider adding
new option to view3dscene (--scene-change-).
All other necessary changes after extending TSceneChange will be enforced
by the compiler.
See [https://castle-engine.io/view3dscene.php#section_command_line_options]
for detailed spec what these scene changes do. }
TSceneChange = (
scNoNormals,
scNoSolidObjects,
scNoConvexFaces,
scConvertInlines
);
TSceneChanges = set of TSceneChange;
{ Transforms scene root node as appropriate.
When you use this on a TCastleSceneCore class, remember to surround
by TCastleSceneCore.BeforeNodesFree and TCastleSceneCore.ChangedAll. }
procedure ChangeNode(SceneChanges: TSceneChanges; Node: TX3DRootNode);
{ Transforms all scenes inside the animation as appropriate. }
procedure ChangeScene(SceneChanges: TSceneChanges; Scene: TCastleScene);
implementation
uses SysUtils, X3DFields;
{ TSceneChangesDo -------------------------------------------------- }
type
TSceneChangesDo = class
public
class procedure NoNormal_Indexed_1(node: TX3DNode);
class procedure NoNormal_ComposedGeometryNode(node: TX3DNode);
class procedure NoNormal_ElevationGrid(node: TX3DNode);
class procedure NoSolid_ShapeHints(node: TX3DNode);
class procedure NoSolid_AbstractGeometry(Node: TX3DNode);
class procedure NoConvex_ShapeHints(node: TX3DNode);
class procedure NoConvex_AbstractGeometry(node: TX3DNode);
class procedure ConvertInlines(ParentNode: TX3DNode; var Node: TX3DNode);
end;
class procedure TSceneChangesDo.NoNormal_Indexed_1(node: TX3DNode);
begin
(Node as TAbstractIndexedNode_1).FdNormalIndex.Items.Clear;
end;
class procedure TSceneChangesDo.NoNormal_ComposedGeometryNode(node: TX3DNode);
begin
(Node as TAbstractComposedGeometryNode).FdNormal.Value := nil;
end;
class procedure TSceneChangesDo.NoNormal_ElevationGrid(node: TX3DNode);
begin
(Node as TElevationGridNode).FdNormal.Value := nil;
end;
class procedure TSceneChangesDo.NoSolid_ShapeHints(node: TX3DNode);
begin
(Node as TShapeHintsNode_1).FdShapeType.Value := SHTYPE_UNKNOWN;
end;
class procedure TSceneChangesDo.NoSolid_AbstractGeometry(Node: TX3DNode);
var
F: TSFBool;
begin
{ We could also just use
(Node as TAbstractGeometryNode).Value := false
but that would be less optimal.
(Better to let scene to call ChangedAll later.) }
F := (Node as TAbstractGeometryNode).SolidField;
if F <> nil then F.Value := false;
end;
class procedure TSceneChangesDo.NoConvex_ShapeHints(node: TX3DNode);
begin
(Node as TShapeHintsNode_1).FdFaceType.Value := FACETYPE_UNKNOWN;
end;
class procedure TSceneChangesDo.NoConvex_AbstractGeometry(node: TX3DNode);
var
F: TSFBool;
begin
F := (Node as TAbstractGeometryNode).ConvexField;
if F <> nil then F.Value := false;
end;
class procedure TSceneChangesDo.ConvertInlines(ParentNode: TX3DNode; var Node: TX3DNode);
var
InlineNode: TInlineNode;
NewGroup: TGroupNode;
begin
if Node is TInlineNode then
begin
// replace Node with new TGroupNode
InlineNode := TInlineNode(Node);
NewGroup := TGroupNode.Create(InlineNode.X3DName, InlineNode.BaseUrl);
Node := NewGroup;
// put Inline contents into new group
if InlineNode.Inlined <> nil then
begin
ChangeNode([scConvertInlines], InlineNode.Inlined);
NewGroup.FdChildren.Assign(InlineNode.Inlined.FdChildren);
end;
end;
end;
{ SceneChange_Xxx functions ---------------------------------------- }
procedure RemoveNodeClass(RootNode: TX3DNode;
NodeClass: TX3DNodeClass; onlyFromActivePart: boolean);
var
Node: TX3DNode;
begin
{ TODO: do this by EnumerateReplaceNodes.
Actually, most usage of FreeRemovingFromAllParents in engine should
be replaced by faster and cleaner EnumerateReplaceNodes?
usuwania node'ow ze sceny lepiej nie robic przez EnumNodes -
nie mozemy zaburzac hierarchii sceny w czasie jej przegladania.}
repeat
node := RootNode.TryFindNode(NodeClass, onlyFromActivePart);
if node = nil then break;
node.FreeRemovingFromAllParents;
until false;
end;
{ TODO: maybe avoid calling costly ChangedAll after scene changes. }
procedure SceneChange_NoNormals(Node: TX3DRootNode);
begin
Node.EnumerateNodes(TAbstractIndexedNode_1,
@TSceneChangesDo(nil).NoNormal_Indexed_1, false);
Node.EnumerateNodes(TAbstractComposedGeometryNode,
@TSceneChangesDo(nil).NoNormal_ComposedGeometryNode, false);
Node.EnumerateNodes(TElevationGridNode,
@TSceneChangesDo(nil).NoNormal_ElevationGrid, false);
{ Do this at the end.
Note that for VRML >= 2.0, most of the Normal nodes were already removed
by NoNormal_ComposedGeometryNode and NoNormal_ElevationGrid anyway. }
RemoveNodeClass(Node, TNormalNode, false);
RemoveNodeClass(Node, TNormalBindingNode_1, false);
end;
procedure SceneChange_NoSolidObjects(Node: TX3DRootNode);
begin
Node.EnumerateNodes(TShapeHintsNode_1,
@TSceneChangesDo(nil).NoSolid_ShapeHints, false);
Node.EnumerateNodes(TAbstractGeometryNode,
@TSceneChangesDo(nil).NoSolid_AbstractGeometry, false);
end;
procedure SceneChange_NoConvexFaces(Node: TX3DRootNode);
var
SH: TShapeHintsNode_1;
begin
Node.EnumerateNodes(TShapeHintsNode_1,
@TSceneChangesDo(nil).NoConvex_ShapeHints, false);
Node.EnumerateNodes(TAbstractGeometryNode,
@TSceneChangesDo(nil).NoConvex_AbstractGeometry, false);
if Node.TryFindNode(TAbstractGeometryNode_1, false) <> nil then
begin
SH := TShapeHintsNode_1.Create('', Node.BaseUrl);
SH.FdFaceType.Value := FACETYPE_UNKNOWN;
Node.FdChildren.Add(0, SH);
end;
end;
procedure SceneChange_ConvertInlines(Node: TX3DRootNode);
begin
Node.EnumerateReplaceChildren(@TSceneChangesDo(nil).ConvertInlines);
end;
{ ChangeScene --------------------------------------------------------------- }
type
TSceneChangeFunction = procedure (Node: TX3DRootNode);
const
SCFunctions: array[TSceneChange]of TSceneChangeFunction =
( @SceneChange_NoNormals,
@SceneChange_NoSolidObjects,
@SceneChange_NoConvexFaces,
@SceneChange_ConvertInlines
);
procedure ChangeNode(SceneChanges: TSceneChanges; Node: TX3DRootNode);
var
SC: TSceneChange;
begin
for SC := Low(SC) to High(SC) do
if SC in SceneChanges then
SCFunctions[SC](Node);
end;
procedure ChangeScene(SceneChanges: TSceneChanges; Scene: TCastleScene);
begin
{ Check SceneChanges, to avoid expensive ChangedAll (that also invalidates
all data prepared in shapes, like octrees) when not needed. }
if (SceneChanges <> []) and (Scene.RootNode <> nil) then
begin
Scene.BeforeNodesFree;
ChangeNode(SceneChanges, Scene.RootNode);
Scene.ChangedAll;
end;
end;
end.
|