Add "expand inlines" option?
(Report on forum if you want this to be finished!)

We have an implementation ready from castle-process-3d-model.
Drawback: VRML 1.0 inlined in X3D doesn't really work,
and generally you should not mix VRML/X3D versions.

Original comments and code:
  - Inlines are resolved. That is, VRML/X3D "Inline" nodes are replaced
    with their actual files' contents. Without this, processing inside
    inlines' content would not be saved, and e.g. using this program with
    data/levels/fountain/fountain_final.wrl would be useless
    (as the actual normal maps would be added inside inline'd fountain.wrl only).

{ Resolve inlines, that is replace all Inline nodes with Group nodes containing
  their contents. }
procedure ResolveInlines(Node: TX3DNode); forward;

{ ResolveInlines ------------------------------------------------------------- }

type
  TEnumerateResolveInlines = class
  public
    class procedure Enumerate(ParentNode: TX3DNode; var Node: TX3DNode);
  end;

class procedure TEnumerateResolveInlines.Enumerate(ParentNode: TX3DNode; var Node: TX3DNode);
var
  G2: TGroupNode;
  G1: TX3DNode;
  Inlined: TX3DNode;
begin
  { Replace VRML 1.0 inlines with VRML 1.0 Group or Separator node.
    Note that TWWWInlineNode_1 actually descends from TInlineNode now,
    so the check for TWWWInlineNode_1 must be 1st. }
  if Node is TWWWInlineNode_1 then
  begin
    TWWWInlineNode_1(Node).LoadInlined(false);
    Inlined := TWWWInlineNode_1(Node).Inlined;

    if Inlined <> nil then
    begin
      if TWWWInlineNode_1(Node).FdSeparate.Value then
        G1 := TSeparatorNode_1.Create(Node.NodeName, Node.BaseUrl) else
        G1 := TGroupNode_1.Create(Node.NodeName, Node.BaseUrl);
      G1.PositionInParent := Node.PositionInParent;
      G1.VRML1ChildAdd(Inlined);
      Node := G1;
    end;
  end else
  { Replace VRML >= 2.0 inlines with VRML 2.0 / X3D Group node }
  if Node is TInlineNode then
  begin
    TInlineNode(Node).LoadInlined(false);
    Inlined := TInlineNode(Node).Inlined;

    if Inlined <> nil then
    begin
      G2 := TGroupNode.Create(Node.NodeName, Node.BaseUrl);
      { update PositionInParent,
        to make the resulting VRML look more similar to original
        (otherwise resolved inline could move up in the file) }
      G2.PositionInParent := Node.PositionInParent;
      G2.FdChildren.Add(Inlined);
      Node := G2;
    end;
  end;
end;

procedure ResolveInlines(Node: TX3DNode);
begin
  Node.EnumerateReplaceChildren(@TEnumerateResolveInlines(nil).Enumerate);
end;
