Merge 0bdcd77961
into 877d278785
This commit is contained in:
commit
f327316279
31
Cargo.toml
31
Cargo.toml
@ -230,6 +230,14 @@ bevy_gilrs = ["bevy_internal/bevy_gilrs"]
|
|||||||
|
|
||||||
# [glTF](https://www.khronos.org/gltf/) support
|
# [glTF](https://www.khronos.org/gltf/) support
|
||||||
bevy_gltf = ["bevy_internal/bevy_gltf", "bevy_asset", "bevy_scene", "bevy_pbr"]
|
bevy_gltf = ["bevy_internal/bevy_gltf", "bevy_asset", "bevy_scene", "bevy_pbr"]
|
||||||
|
# [FBX](https://en.wikipedia.org/wiki/FBX)
|
||||||
|
fbx = [
|
||||||
|
"bevy_internal/bevy_fbx",
|
||||||
|
"bevy_asset",
|
||||||
|
"bevy_scene",
|
||||||
|
"bevy_pbr",
|
||||||
|
"bevy_animation",
|
||||||
|
]
|
||||||
|
|
||||||
# Adds PBR rendering
|
# Adds PBR rendering
|
||||||
bevy_pbr = [
|
bevy_pbr = [
|
||||||
@ -1196,6 +1204,17 @@ description = "Loads and renders a glTF file as a scene, including the gltf extr
|
|||||||
category = "3D Rendering"
|
category = "3D Rendering"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "load_fbx"
|
||||||
|
path = "examples/3d/load_fbx.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.load_fbx]
|
||||||
|
name = "Load FBX"
|
||||||
|
description = "Loads and renders an FBX file as a scene"
|
||||||
|
category = "3D Rendering"
|
||||||
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "query_gltf_primitives"
|
name = "query_gltf_primitives"
|
||||||
path = "examples/3d/query_gltf_primitives.rs"
|
path = "examples/3d/query_gltf_primitives.rs"
|
||||||
@ -3223,6 +3242,18 @@ description = "A simple way to view glTF models with Bevy. Just run `cargo run -
|
|||||||
category = "Tools"
|
category = "Tools"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "scene_viewer_fbx"
|
||||||
|
path = "examples/tools/scene_viewer_fbx/main.rs"
|
||||||
|
required-features = ["fbx"]
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.scene_viewer_fbx]
|
||||||
|
name = "FBX Scene Viewer"
|
||||||
|
description = "A simple way to view FBX models with Bevy. Just run `cargo run --release --example scene_viewer_fbx --features=fbx /path/to/model.fbx`, replacing the path as appropriate. Provides enhanced controls for FBX-specific features like material inspection and texture debugging"
|
||||||
|
category = "Tools"
|
||||||
|
wasm = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "gamepad_viewer"
|
name = "gamepad_viewer"
|
||||||
path = "examples/tools/gamepad_viewer.rs"
|
path = "examples/tools/gamepad_viewer.rs"
|
||||||
|
BIN
assets/models/cube/cube.fbx
Normal file
BIN
assets/models/cube/cube.fbx
Normal file
Binary file not shown.
801
assets/models/cube_anim.fbx
Normal file
801
assets/models/cube_anim.fbx
Normal file
@ -0,0 +1,801 @@
|
|||||||
|
; FBX 7.7.0 project file
|
||||||
|
; ----------------------------------------------------
|
||||||
|
|
||||||
|
FBXHeaderExtension: {
|
||||||
|
FBXHeaderVersion: 1004
|
||||||
|
FBXVersion: 7700
|
||||||
|
CreationTimeStamp: {
|
||||||
|
Version: 1000
|
||||||
|
Year: 2023
|
||||||
|
Month: 9
|
||||||
|
Day: 7
|
||||||
|
Hour: 22
|
||||||
|
Minute: 17
|
||||||
|
Second: 31
|
||||||
|
Millisecond: 940
|
||||||
|
}
|
||||||
|
Creator: "FBX SDK/FBX Plugins version 2020.3"
|
||||||
|
OtherFlags: {
|
||||||
|
TCDefinition: 127
|
||||||
|
}
|
||||||
|
SceneInfo: "SceneInfo::GlobalInfo", "UserData" {
|
||||||
|
Type: "UserData"
|
||||||
|
Version: 100
|
||||||
|
MetaData: {
|
||||||
|
Version: 100
|
||||||
|
Title: ""
|
||||||
|
Subject: ""
|
||||||
|
Author: ""
|
||||||
|
Keywords: ""
|
||||||
|
Revision: ""
|
||||||
|
Comment: ""
|
||||||
|
}
|
||||||
|
Properties70: {
|
||||||
|
P: "DocumentUrl", "KString", "Url", "", "D:\Dev\clean\ufbx-rust\tests\data\cube_anim.fbx"
|
||||||
|
P: "SrcDocumentUrl", "KString", "Url", "", "D:\Dev\clean\ufbx-rust\tests\data\cube_anim.fbx"
|
||||||
|
P: "Original", "Compound", "", ""
|
||||||
|
P: "Original|ApplicationVendor", "KString", "", "", "Autodesk"
|
||||||
|
P: "Original|ApplicationName", "KString", "", "", "Maya"
|
||||||
|
P: "Original|ApplicationVersion", "KString", "", "", "2023"
|
||||||
|
P: "Original|DateTime_GMT", "DateTime", "", "", "07/09/2023 19:17:31.937"
|
||||||
|
P: "Original|FileName", "KString", "", "", "D:\Dev\clean\ufbx-rust\tests\data\cube_anim.fbx"
|
||||||
|
P: "LastSaved", "Compound", "", ""
|
||||||
|
P: "LastSaved|ApplicationVendor", "KString", "", "", "Autodesk"
|
||||||
|
P: "LastSaved|ApplicationName", "KString", "", "", "Maya"
|
||||||
|
P: "LastSaved|ApplicationVersion", "KString", "", "", "2023"
|
||||||
|
P: "LastSaved|DateTime_GMT", "DateTime", "", "", "07/09/2023 19:17:31.937"
|
||||||
|
P: "Original|ApplicationActiveProject", "KString", "", "", "D:\Dev\clean\ufbx-rust\tests\data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GlobalSettings: {
|
||||||
|
Version: 1000
|
||||||
|
Properties70: {
|
||||||
|
P: "UpAxis", "int", "Integer", "",1
|
||||||
|
P: "UpAxisSign", "int", "Integer", "",1
|
||||||
|
P: "FrontAxis", "int", "Integer", "",2
|
||||||
|
P: "FrontAxisSign", "int", "Integer", "",1
|
||||||
|
P: "CoordAxis", "int", "Integer", "",0
|
||||||
|
P: "CoordAxisSign", "int", "Integer", "",1
|
||||||
|
P: "OriginalUpAxis", "int", "Integer", "",1
|
||||||
|
P: "OriginalUpAxisSign", "int", "Integer", "",1
|
||||||
|
P: "UnitScaleFactor", "double", "Number", "",1
|
||||||
|
P: "OriginalUnitScaleFactor", "double", "Number", "",1
|
||||||
|
P: "AmbientColor", "ColorRGB", "Color", "",0,0,0
|
||||||
|
P: "DefaultCamera", "KString", "", "", "Producer Perspective"
|
||||||
|
P: "TimeMode", "enum", "", "",11
|
||||||
|
P: "TimeProtocol", "enum", "", "",2
|
||||||
|
P: "SnapOnFrameMode", "enum", "", "",0
|
||||||
|
P: "TimeSpanStart", "KTime", "Time", "",0
|
||||||
|
P: "TimeSpanStop", "KTime", "Time", "",192442325000
|
||||||
|
P: "CustomFrameRate", "double", "Number", "",-1
|
||||||
|
P: "TimeMarker", "Compound", "", ""
|
||||||
|
P: "CurrentTimeMarker", "int", "Integer", "",-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Documents Description
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Documents: {
|
||||||
|
Count: 1
|
||||||
|
Document: 2244722366480, "", "Scene" {
|
||||||
|
Properties70: {
|
||||||
|
P: "SourceObject", "object", "", ""
|
||||||
|
P: "ActiveAnimStackName", "KString", "", "", "Take 001"
|
||||||
|
}
|
||||||
|
RootNode: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Document References
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
References: {
|
||||||
|
}
|
||||||
|
|
||||||
|
; Object definitions
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Definitions: {
|
||||||
|
Version: 100
|
||||||
|
Count: 24
|
||||||
|
ObjectType: "GlobalSettings" {
|
||||||
|
Count: 1
|
||||||
|
}
|
||||||
|
ObjectType: "AnimationStack" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxAnimStack" {
|
||||||
|
Properties70: {
|
||||||
|
P: "Description", "KString", "", "", ""
|
||||||
|
P: "LocalStart", "KTime", "Time", "",0
|
||||||
|
P: "LocalStop", "KTime", "Time", "",0
|
||||||
|
P: "ReferenceStart", "KTime", "Time", "",0
|
||||||
|
P: "ReferenceStop", "KTime", "Time", "",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "AnimationLayer" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxAnimLayer" {
|
||||||
|
Properties70: {
|
||||||
|
P: "Weight", "Number", "", "A",100
|
||||||
|
P: "Mute", "bool", "", "",0
|
||||||
|
P: "Solo", "bool", "", "",0
|
||||||
|
P: "Lock", "bool", "", "",0
|
||||||
|
P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8
|
||||||
|
P: "BlendMode", "enum", "", "",0
|
||||||
|
P: "RotationAccumulationMode", "enum", "", "",0
|
||||||
|
P: "ScaleAccumulationMode", "enum", "", "",0
|
||||||
|
P: "BlendModeBypass", "ULongLong", "", "",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "Geometry" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxMesh" {
|
||||||
|
Properties70: {
|
||||||
|
P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8
|
||||||
|
P: "BBoxMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "BBoxMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Primary Visibility", "bool", "", "",1
|
||||||
|
P: "Casts Shadows", "bool", "", "",1
|
||||||
|
P: "Receive Shadows", "bool", "", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "Material" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxSurfaceLambert" {
|
||||||
|
Properties70: {
|
||||||
|
P: "ShadingModel", "KString", "", "", "Lambert"
|
||||||
|
P: "MultiLayer", "bool", "", "",0
|
||||||
|
P: "EmissiveColor", "Color", "", "A",0,0,0
|
||||||
|
P: "EmissiveFactor", "Number", "", "A",1
|
||||||
|
P: "AmbientColor", "Color", "", "A",0.2,0.2,0.2
|
||||||
|
P: "AmbientFactor", "Number", "", "A",1
|
||||||
|
P: "DiffuseColor", "Color", "", "A",0.8,0.8,0.8
|
||||||
|
P: "DiffuseFactor", "Number", "", "A",1
|
||||||
|
P: "Bump", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "NormalMap", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "BumpFactor", "double", "Number", "",1
|
||||||
|
P: "TransparentColor", "Color", "", "A",0,0,0
|
||||||
|
P: "TransparencyFactor", "Number", "", "A",0
|
||||||
|
P: "DisplacementColor", "ColorRGB", "Color", "",0,0,0
|
||||||
|
P: "DisplacementFactor", "double", "Number", "",1
|
||||||
|
P: "VectorDisplacementColor", "ColorRGB", "Color", "",0,0,0
|
||||||
|
P: "VectorDisplacementFactor", "double", "Number", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "AnimationCurveNode" {
|
||||||
|
Count: 5
|
||||||
|
PropertyTemplate: "FbxAnimCurveNode" {
|
||||||
|
Properties70: {
|
||||||
|
P: "d", "Compound", "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "AnimationCurve" {
|
||||||
|
Count: 13
|
||||||
|
}
|
||||||
|
ObjectType: "Model" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxNode" {
|
||||||
|
Properties70: {
|
||||||
|
P: "QuaternionInterpolate", "enum", "", "",0
|
||||||
|
P: "RotationOffset", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "ScalingOffset", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "ScalingPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TranslationActive", "bool", "", "",0
|
||||||
|
P: "TranslationMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TranslationMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TranslationMinX", "bool", "", "",0
|
||||||
|
P: "TranslationMinY", "bool", "", "",0
|
||||||
|
P: "TranslationMinZ", "bool", "", "",0
|
||||||
|
P: "TranslationMaxX", "bool", "", "",0
|
||||||
|
P: "TranslationMaxY", "bool", "", "",0
|
||||||
|
P: "TranslationMaxZ", "bool", "", "",0
|
||||||
|
P: "RotationOrder", "enum", "", "",0
|
||||||
|
P: "RotationSpaceForLimitOnly", "bool", "", "",0
|
||||||
|
P: "RotationStiffnessX", "double", "Number", "",0
|
||||||
|
P: "RotationStiffnessY", "double", "Number", "",0
|
||||||
|
P: "RotationStiffnessZ", "double", "Number", "",0
|
||||||
|
P: "AxisLen", "double", "Number", "",10
|
||||||
|
P: "PreRotation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "PostRotation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationActive", "bool", "", "",0
|
||||||
|
P: "RotationMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationMinX", "bool", "", "",0
|
||||||
|
P: "RotationMinY", "bool", "", "",0
|
||||||
|
P: "RotationMinZ", "bool", "", "",0
|
||||||
|
P: "RotationMaxX", "bool", "", "",0
|
||||||
|
P: "RotationMaxY", "bool", "", "",0
|
||||||
|
P: "RotationMaxZ", "bool", "", "",0
|
||||||
|
P: "InheritType", "enum", "", "",0
|
||||||
|
P: "ScalingActive", "bool", "", "",0
|
||||||
|
P: "ScalingMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "ScalingMax", "Vector3D", "Vector", "",1,1,1
|
||||||
|
P: "ScalingMinX", "bool", "", "",0
|
||||||
|
P: "ScalingMinY", "bool", "", "",0
|
||||||
|
P: "ScalingMinZ", "bool", "", "",0
|
||||||
|
P: "ScalingMaxX", "bool", "", "",0
|
||||||
|
P: "ScalingMaxY", "bool", "", "",0
|
||||||
|
P: "ScalingMaxZ", "bool", "", "",0
|
||||||
|
P: "GeometricTranslation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "GeometricRotation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "GeometricScaling", "Vector3D", "Vector", "",1,1,1
|
||||||
|
P: "MinDampRangeX", "double", "Number", "",0
|
||||||
|
P: "MinDampRangeY", "double", "Number", "",0
|
||||||
|
P: "MinDampRangeZ", "double", "Number", "",0
|
||||||
|
P: "MaxDampRangeX", "double", "Number", "",0
|
||||||
|
P: "MaxDampRangeY", "double", "Number", "",0
|
||||||
|
P: "MaxDampRangeZ", "double", "Number", "",0
|
||||||
|
P: "MinDampStrengthX", "double", "Number", "",0
|
||||||
|
P: "MinDampStrengthY", "double", "Number", "",0
|
||||||
|
P: "MinDampStrengthZ", "double", "Number", "",0
|
||||||
|
P: "MaxDampStrengthX", "double", "Number", "",0
|
||||||
|
P: "MaxDampStrengthY", "double", "Number", "",0
|
||||||
|
P: "MaxDampStrengthZ", "double", "Number", "",0
|
||||||
|
P: "PreferedAngleX", "double", "Number", "",0
|
||||||
|
P: "PreferedAngleY", "double", "Number", "",0
|
||||||
|
P: "PreferedAngleZ", "double", "Number", "",0
|
||||||
|
P: "LookAtProperty", "object", "", ""
|
||||||
|
P: "UpVectorProperty", "object", "", ""
|
||||||
|
P: "Show", "bool", "", "",1
|
||||||
|
P: "NegativePercentShapeSupport", "bool", "", "",1
|
||||||
|
P: "DefaultAttributeIndex", "int", "Integer", "",-1
|
||||||
|
P: "Freeze", "bool", "", "",0
|
||||||
|
P: "LODBox", "bool", "", "",0
|
||||||
|
P: "Lcl Translation", "Lcl Translation", "", "A",0,0,0
|
||||||
|
P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0
|
||||||
|
P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
|
||||||
|
P: "Visibility", "Visibility", "", "A",1
|
||||||
|
P: "Visibility Inheritance", "Visibility Inheritance", "", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Object properties
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Objects: {
|
||||||
|
Geometry: 2245309148656, "Geometry::", "Mesh" {
|
||||||
|
Vertices: *24 {
|
||||||
|
a: -0.5,-0.5,0.5,0.5,-0.5,0.5,-0.5,0.5,0.5,0.5,0.5,0.5,-0.5,0.5,-0.5,0.5,0.5,-0.5,-0.5,-0.5,-0.5,0.5,-0.5,-0.5
|
||||||
|
}
|
||||||
|
PolygonVertexIndex: *24 {
|
||||||
|
a: 0,1,3,-3,2,3,5,-5,4,5,7,-7,6,7,1,-1,1,7,5,-4,6,0,2,-5
|
||||||
|
}
|
||||||
|
Edges: *12 {
|
||||||
|
a: 0,2,6,10,3,1,7,5,11,9,15,13
|
||||||
|
}
|
||||||
|
GeometryVersion: 124
|
||||||
|
LayerElementNormal: 0 {
|
||||||
|
Version: 102
|
||||||
|
Name: ""
|
||||||
|
MappingInformationType: "ByPolygonVertex"
|
||||||
|
ReferenceInformationType: "Direct"
|
||||||
|
Normals: *72 {
|
||||||
|
a: 0,0,1,0,0,1,0,0,1,0,0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,1,0,0,1,0,0,1,0,0,1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0
|
||||||
|
}
|
||||||
|
NormalsW: *24 {
|
||||||
|
a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerElementBinormal: 0 {
|
||||||
|
Version: 102
|
||||||
|
Name: "map1"
|
||||||
|
MappingInformationType: "ByPolygonVertex"
|
||||||
|
ReferenceInformationType: "Direct"
|
||||||
|
Binormals: *72 {
|
||||||
|
a: 0,1,-0,0,1,-0,0,1,-0,0,1,-0,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,0,1,0,0,1,0,0,1,0,0,1,-0,1,0,-0,1,0,0,1,-0,-0,1,0,0,1,0,0,1,0,0,1,0,0,1,0
|
||||||
|
}
|
||||||
|
BinormalsW: *24 {
|
||||||
|
a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
LayerElementTangent: 0 {
|
||||||
|
Version: 102
|
||||||
|
Name: "map1"
|
||||||
|
MappingInformationType: "ByPolygonVertex"
|
||||||
|
ReferenceInformationType: "Direct"
|
||||||
|
Tangents: *72 {
|
||||||
|
a: 1,-0,-0,1,-0,0,1,-0,0,1,-0,0,1,-0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,-0,1,0,-0,1,0,-0,1,0,-0,0,0,-1,0,0,-1,0,-0,-1,0,0,-1,0,-0,1,0,-0,1,0,-0,1,0,-0,1
|
||||||
|
}
|
||||||
|
TangentsW: *24 {
|
||||||
|
a: 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerElementUV: 0 {
|
||||||
|
Version: 101
|
||||||
|
Name: "map1"
|
||||||
|
MappingInformationType: "ByPolygonVertex"
|
||||||
|
ReferenceInformationType: "IndexToDirect"
|
||||||
|
UV: *28 {
|
||||||
|
a: 0.375,0,0.625,0,0.375,0.25,0.625,0.25,0.375,0.5,0.625,0.5,0.375,0.75,0.625,0.75,0.375,1,0.625,1,0.875,0,0.875,0.25,0.125,0,0.125,0.25
|
||||||
|
}
|
||||||
|
UVIndex: *24 {
|
||||||
|
a: 0,1,3,2,2,3,5,4,4,5,7,6,6,7,9,8,1,10,11,3,12,0,2,13
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerElementMaterial: 0 {
|
||||||
|
Version: 101
|
||||||
|
Name: ""
|
||||||
|
MappingInformationType: "AllSame"
|
||||||
|
ReferenceInformationType: "IndexToDirect"
|
||||||
|
Materials: *1 {
|
||||||
|
a: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layer: 0 {
|
||||||
|
Version: 100
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementNormal"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementBinormal"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementTangent"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementMaterial"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementUV"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Model: 2244692774032, "Model::pCube1", "Mesh" {
|
||||||
|
Version: 232
|
||||||
|
Properties70: {
|
||||||
|
P: "RotationActive", "bool", "", "",1
|
||||||
|
P: "InheritType", "enum", "", "",1
|
||||||
|
P: "ScalingMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "DefaultAttributeIndex", "int", "Integer", "",0
|
||||||
|
P: "Lcl Translation", "Lcl Translation", "", "A+",0,0.518518518518518,0
|
||||||
|
P: "Lcl Rotation", "Lcl Rotation", "", "A+",11.6666666666667,11.6666666666667,0
|
||||||
|
P: "Lcl Scaling", "Lcl Scaling", "", "A+",1.05185185185185,1.1037037037037,1.15555555555556
|
||||||
|
P: "currentUVSet", "KString", "", "U", "map1"
|
||||||
|
}
|
||||||
|
Shading: T
|
||||||
|
Culling: "CullingOff"
|
||||||
|
}
|
||||||
|
Material: 2242872361376, "Material::lambert1", "" {
|
||||||
|
Version: 102
|
||||||
|
ShadingModel: "lambert"
|
||||||
|
MultiLayer: 0
|
||||||
|
Properties70: {
|
||||||
|
P: "AmbientColor", "Color", "", "A",0,0,0
|
||||||
|
P: "DiffuseColor", "Color", "", "A+",0.740740716457367,0.259259253740311,0
|
||||||
|
P: "DiffuseFactor", "Number", "", "A",0.800000011920929
|
||||||
|
P: "TransparencyFactor", "Number", "", "A",1
|
||||||
|
P: "Emissive", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Ambient", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Diffuse", "Vector3D", "Vector", "",0.592592581996211,0.20740740608286,0
|
||||||
|
P: "Opacity", "double", "Number", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationStack: 2243155095872, "AnimStack::Take 001", "" {
|
||||||
|
Properties70: {
|
||||||
|
P: "LocalStop", "KTime", "Time", "",38488465000
|
||||||
|
P: "ReferenceStop", "KTime", "Time", "",38488465000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096615744, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 1,0
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096607744, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,1
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096613984, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,0
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096609024, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Constant|ConstantStandard, Constant|ConstantStandard
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 2,2
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightSlope:0, NextLeftSlope:0, RightWeight:0.333333, NextLeftWeight:0.333333, RightVelocity:0, NextLeftVelocity:0; RightSlope:0, NextLeftSlope:0, RightWeight:0.333333, NextLeftWeight:0.333333, RightVelocity:0, NextLeftVelocity:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096609184, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,0
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096614944, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,2
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096614304, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,0
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096609824, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 1,1.2
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096613504, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 1,1.4
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096615424, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 1,1.6
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096609344, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,45
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096607264, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,45
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurve: 2243096615104, "AnimCurve::", "" {
|
||||||
|
Default: 0
|
||||||
|
KeyVer: 4009
|
||||||
|
KeyTime: *2 {
|
||||||
|
a: 0,23093079000
|
||||||
|
}
|
||||||
|
KeyValueFloat: *2 {
|
||||||
|
a: 0,0
|
||||||
|
}
|
||||||
|
;KeyAttrFlags: Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive, Cubic|TangeantAuto|GenericTimeIndependent|GenericClampProgressive
|
||||||
|
KeyAttrFlags: *2 {
|
||||||
|
a: 24840,24840
|
||||||
|
}
|
||||||
|
;KeyAttrDataFloat: RightAuto:0, NextLeftAuto:0; RightAuto:0, NextLeftAuto:0
|
||||||
|
KeyAttrDataFloat: *8 {
|
||||||
|
a: 0,0,218434821,0,0,0,218434821,0
|
||||||
|
}
|
||||||
|
KeyAttrRefCount: *2 {
|
||||||
|
a: 1,1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurveNode: 2243155097120, "AnimCurveNode::DiffuseColor", "" {
|
||||||
|
Properties70: {
|
||||||
|
P: "d|X", "Number", "", "A",0.740740716457367
|
||||||
|
P: "d|Y", "Number", "", "A",0.259259253740311
|
||||||
|
P: "d|Z", "Number", "", "A",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurveNode: 2243155095456, "AnimCurveNode::Visibility", "" {
|
||||||
|
Properties70: {
|
||||||
|
P: "d|Visibility", "Visibility", "", "A",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurveNode: 2243155095040, "AnimCurveNode::T", "" {
|
||||||
|
Properties70: {
|
||||||
|
P: "d|X", "Number", "", "A",0
|
||||||
|
P: "d|Y", "Number", "", "A",0.518518518518518
|
||||||
|
P: "d|Z", "Number", "", "A",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurveNode: 2243155095248, "AnimCurveNode::S", "" {
|
||||||
|
Properties70: {
|
||||||
|
P: "d|X", "Number", "", "A",1.05185185185185
|
||||||
|
P: "d|Y", "Number", "", "A",1.1037037037037
|
||||||
|
P: "d|Z", "Number", "", "A",1.15555555555556
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationCurveNode: 2243155089008, "AnimCurveNode::R", "" {
|
||||||
|
Properties70: {
|
||||||
|
P: "d|X", "Number", "", "A",11.6666666666667
|
||||||
|
P: "d|Y", "Number", "", "A",11.6666666666667
|
||||||
|
P: "d|Z", "Number", "", "A",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AnimationLayer: 2245017641168, "AnimLayer::BaseLayer", "" {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Object connections
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Connections: {
|
||||||
|
|
||||||
|
;Model::pCube1, Model::RootNode
|
||||||
|
C: "OO",2244692774032,0
|
||||||
|
|
||||||
|
;AnimLayer::BaseLayer, AnimStack::Take 001
|
||||||
|
C: "OO",2245017641168,2243155095872
|
||||||
|
|
||||||
|
;AnimCurveNode::DiffuseColor, AnimLayer::BaseLayer
|
||||||
|
C: "OO",2243155097120,2245017641168
|
||||||
|
|
||||||
|
;AnimCurveNode::Visibility, AnimLayer::BaseLayer
|
||||||
|
C: "OO",2243155095456,2245017641168
|
||||||
|
|
||||||
|
;AnimCurveNode::T, AnimLayer::BaseLayer
|
||||||
|
C: "OO",2243155095040,2245017641168
|
||||||
|
|
||||||
|
;AnimCurveNode::S, AnimLayer::BaseLayer
|
||||||
|
C: "OO",2243155095248,2245017641168
|
||||||
|
|
||||||
|
;AnimCurveNode::R, AnimLayer::BaseLayer
|
||||||
|
C: "OO",2243155089008,2245017641168
|
||||||
|
|
||||||
|
;AnimCurveNode::DiffuseColor, Material::lambert1
|
||||||
|
C: "OP",2243155097120,2242872361376, "DiffuseColor"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::DiffuseColor
|
||||||
|
C: "OP",2243096615744,2243155097120, "d|X"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::DiffuseColor
|
||||||
|
C: "OP",2243096607744,2243155097120, "d|Y"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::DiffuseColor
|
||||||
|
C: "OP",2243096613984,2243155097120, "d|Z"
|
||||||
|
|
||||||
|
;Geometry::, Model::pCube1
|
||||||
|
C: "OO",2245309148656,2244692774032
|
||||||
|
|
||||||
|
;Material::lambert1, Model::pCube1
|
||||||
|
C: "OO",2242872361376,2244692774032
|
||||||
|
|
||||||
|
;AnimCurveNode::T, Model::pCube1
|
||||||
|
C: "OP",2243155095040,2244692774032, "Lcl Translation"
|
||||||
|
|
||||||
|
;AnimCurveNode::R, Model::pCube1
|
||||||
|
C: "OP",2243155089008,2244692774032, "Lcl Rotation"
|
||||||
|
|
||||||
|
;AnimCurveNode::S, Model::pCube1
|
||||||
|
C: "OP",2243155095248,2244692774032, "Lcl Scaling"
|
||||||
|
|
||||||
|
;AnimCurveNode::Visibility, Model::pCube1
|
||||||
|
C: "OP",2243155095456,2244692774032, "Visibility"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::Visibility
|
||||||
|
C: "OP",2243096609024,2243155095456, "d|Visibility"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::T
|
||||||
|
C: "OP",2243096609184,2243155095040, "d|X"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::T
|
||||||
|
C: "OP",2243096614944,2243155095040, "d|Y"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::T
|
||||||
|
C: "OP",2243096614304,2243155095040, "d|Z"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::S
|
||||||
|
C: "OP",2243096609824,2243155095248, "d|X"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::S
|
||||||
|
C: "OP",2243096613504,2243155095248, "d|Y"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::S
|
||||||
|
C: "OP",2243096615424,2243155095248, "d|Z"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::R
|
||||||
|
C: "OP",2243096609344,2243155089008, "d|X"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::R
|
||||||
|
C: "OP",2243096607264,2243155089008, "d|Y"
|
||||||
|
|
||||||
|
;AnimCurve::, AnimCurveNode::R
|
||||||
|
C: "OP",2243096615104,2243155089008, "d|Z"
|
||||||
|
}
|
||||||
|
;Takes section
|
||||||
|
;----------------------------------------------------
|
||||||
|
|
||||||
|
Takes: {
|
||||||
|
Current: "Take 001"
|
||||||
|
Take: "Take 001" {
|
||||||
|
FileName: "Take_001.tak"
|
||||||
|
LocalTime: 0,38488465000
|
||||||
|
ReferenceTime: 0,38488465000
|
||||||
|
}
|
||||||
|
}
|
BIN
assets/models/instanced_materials.fbx
Normal file
BIN
assets/models/instanced_materials.fbx
Normal file
Binary file not shown.
44
crates/bevy_fbx/Cargo.toml
Normal file
44
crates/bevy_fbx/Cargo.toml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
[package]
|
||||||
|
name = "bevy_fbx"
|
||||||
|
version = "0.17.0-dev"
|
||||||
|
edition = "2024"
|
||||||
|
description = "Bevy Engine FBX loading"
|
||||||
|
homepage = "https://bevyengine.org"
|
||||||
|
repository = "https://github.com/bevyengine/bevy"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["bevy"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
|
||||||
|
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
|
||||||
|
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
|
||||||
|
bevy_scene = { path = "../bevy_scene", version = "0.17.0-dev", features = [
|
||||||
|
"bevy_render",
|
||||||
|
] }
|
||||||
|
bevy_render = { path = "../bevy_render", version = "0.17.0-dev" }
|
||||||
|
bevy_pbr = { path = "../bevy_pbr", version = "0.17.0-dev" }
|
||||||
|
bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
|
||||||
|
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
|
||||||
|
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
|
||||||
|
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
|
||||||
|
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
|
||||||
|
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
] }
|
||||||
|
bevy_animation = { path = "../bevy_animation", version = "0.17.0-dev" }
|
||||||
|
bevy_color = { path = "../bevy_color", version = "0.17.0-dev" }
|
||||||
|
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
thiserror = "1"
|
||||||
|
tracing = { version = "0.1", default-features = false, features = ["std"] }
|
||||||
|
ufbx = "0.8"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bevy_log = { path = "../bevy_log", version = "0.17.0-dev" }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
|
||||||
|
all-features = true
|
139
crates/bevy_fbx/README.md
Normal file
139
crates/bevy_fbx/README.md
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# Bevy FBX
|
||||||
|
|
||||||
|
A Bevy plugin for loading FBX files using the [ufbx](https://github.com/ufbx/ufbx) library.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ **Mesh Loading**: Load 3D meshes with vertices, normals, UVs, and indices
|
||||||
|
- ✅ **Material Support**: Enhanced PBR material loading with texture application
|
||||||
|
- ✅ **Texture Application**: Automatic application of textures to StandardMaterial
|
||||||
|
- Base color (diffuse) textures
|
||||||
|
- Normal maps
|
||||||
|
- Metallic/roughness textures
|
||||||
|
- Emission textures
|
||||||
|
- Ambient occlusion textures
|
||||||
|
- ✅ **Skinning Data**: Complete skinning support with joint weights and inverse bind matrices
|
||||||
|
- ✅ **Node Hierarchy**: Basic scene graph support
|
||||||
|
- ⚠️ **Animations**: Framework in place, temporarily disabled due to ufbx API compatibility
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Enable the Feature
|
||||||
|
|
||||||
|
FBX support is an optional feature in Bevy. Add it to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
bevy = { version = "0.16", features = ["fbx"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loading FBX Files
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use bevy::fbx::FbxAssetLabel;
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
// Load an FBX file
|
||||||
|
let fbx_handle: Handle<bevy::fbx::Fbx> = asset_server.load("models/my_model.fbx");
|
||||||
|
|
||||||
|
// Spawn the FBX scene
|
||||||
|
commands.spawn(SceneRoot(fbx_handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Accessing Individual Assets
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use bevy::fbx::{Fbx, FbxAssetLabel};
|
||||||
|
|
||||||
|
fn access_fbx_assets(
|
||||||
|
fbx_assets: Res<Assets<Fbx>>,
|
||||||
|
fbx_handle: Handle<Fbx>,
|
||||||
|
) {
|
||||||
|
if let Some(fbx) = fbx_assets.get(&fbx_handle) {
|
||||||
|
// Access meshes
|
||||||
|
for mesh_handle in &fbx.meshes {
|
||||||
|
println!("Found mesh: {:?}", mesh_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access materials
|
||||||
|
for material_handle in &fbx.materials {
|
||||||
|
println!("Found material: {:?}", material_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access skins (for skeletal animation)
|
||||||
|
for skin_handle in &fbx.skins {
|
||||||
|
println!("Found skin: {:?}", skin_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access animation clips
|
||||||
|
for animation_handle in &fbx.animation_clips {
|
||||||
|
println!("Found animation: {:?}", animation_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Asset Labels
|
||||||
|
|
||||||
|
You can load specific parts of an FBX file using asset labels:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Load a specific mesh by index
|
||||||
|
let mesh: Handle<Mesh> = asset_server.load("model.fbx#Mesh0");
|
||||||
|
|
||||||
|
// Load a specific material by index
|
||||||
|
let material: Handle<StandardMaterial> = asset_server.load("model.fbx#Material0");
|
||||||
|
|
||||||
|
// Load a specific skin by index
|
||||||
|
let skin: Handle<bevy::fbx::FbxSkin> = asset_server.load("model.fbx#Skin0");
|
||||||
|
|
||||||
|
// Load a specific animation by index
|
||||||
|
let animation: Handle<AnimationClip> = asset_server.load("model.fbx#Animation0");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported FBX Features
|
||||||
|
|
||||||
|
- **Geometry**: Triangulated meshes with positions, normals, UVs
|
||||||
|
- **Materials**: Enhanced PBR properties with automatic texture application
|
||||||
|
- Base color, metallic, roughness, emission values
|
||||||
|
- Automatic extraction from FBX material properties
|
||||||
|
- **Textures**: Complete texture support with automatic application to StandardMaterial
|
||||||
|
- Base color (diffuse) textures → `base_color_texture`
|
||||||
|
- Normal maps → `normal_map_texture`
|
||||||
|
- Metallic textures → `metallic_roughness_texture`
|
||||||
|
- Roughness textures → `metallic_roughness_texture`
|
||||||
|
- Emission textures → `emissive_texture`
|
||||||
|
- Ambient occlusion textures → `occlusion_texture`
|
||||||
|
- **Skinning**: Joint weights, indices, and inverse bind matrices
|
||||||
|
- **Hierarchy**: Node transforms and basic parent-child relationships
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- **Animations**: Framework in place but temporarily disabled due to ufbx API compatibility
|
||||||
|
- **Complex Materials**: Advanced material features beyond PBR are not supported
|
||||||
|
- **FBX-Specific Features**: Some proprietary FBX features may not be available
|
||||||
|
- **Performance**: Large files may have performance implications during loading
|
||||||
|
- **Texture Formats**: Only common image formats supported by Bevy are loaded
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
See `examples/3d/load_fbx.rs` for a complete example of loading and displaying FBX files.
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
This plugin uses the [ufbx](https://github.com/ufbx/ufbx) library, which provides:
|
||||||
|
- Fast and reliable FBX parsing
|
||||||
|
- Support for FBX versions 6.0 and later
|
||||||
|
- Memory-safe C API with Rust bindings
|
||||||
|
- Comprehensive geometry and animation support
|
||||||
|
|
||||||
|
The plugin follows Bevy's asset loading patterns and integrates seamlessly with the existing rendering pipeline.
|
64
crates/bevy_fbx/src/label.rs
Normal file
64
crates/bevy_fbx/src/label.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//! Labels that can be used to load part of an FBX asset
|
||||||
|
|
||||||
|
use bevy_asset::AssetPath;
|
||||||
|
|
||||||
|
/// Labels that can be used to load part of an FBX asset
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum FbxAssetLabel {
|
||||||
|
/// `Scene{}`: FBX Scene as a Bevy [`Scene`](bevy_scene::Scene)
|
||||||
|
Scene(usize),
|
||||||
|
/// `Mesh{}`: FBX Mesh as a Bevy [`Mesh`](bevy_mesh::Mesh)
|
||||||
|
Mesh(usize),
|
||||||
|
/// `Material{}`: FBX material as a Bevy [`StandardMaterial`](bevy_pbr::StandardMaterial)
|
||||||
|
Material(usize),
|
||||||
|
/// `Animation{}`: FBX animation as a Bevy [`AnimationClip`](bevy_animation::AnimationClip)
|
||||||
|
Animation(usize),
|
||||||
|
/// `AnimationStack{}`: FBX animation stack with multiple layers
|
||||||
|
AnimationStack(usize),
|
||||||
|
/// `Skeleton{}`: FBX skeleton for skeletal animation
|
||||||
|
Skeleton(usize),
|
||||||
|
/// `Node{}`: Individual FBX node in the scene hierarchy
|
||||||
|
Node(usize),
|
||||||
|
/// `Skin{}`: FBX skin for skeletal animation
|
||||||
|
Skin(usize),
|
||||||
|
/// `Light{}`: FBX light definition
|
||||||
|
Light(usize),
|
||||||
|
/// `Camera{}`: FBX camera definition
|
||||||
|
Camera(usize),
|
||||||
|
/// `Texture{}`: FBX texture reference
|
||||||
|
Texture(usize),
|
||||||
|
/// `DefaultScene`: Main scene with all objects
|
||||||
|
DefaultScene,
|
||||||
|
/// `DefaultMaterial`: Fallback material used when no material is present
|
||||||
|
DefaultMaterial,
|
||||||
|
/// `RootNode`: Root node of the scene hierarchy
|
||||||
|
RootNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for FbxAssetLabel {
|
||||||
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FbxAssetLabel::Scene(index) => f.write_str(&format!("Scene{index}")),
|
||||||
|
FbxAssetLabel::Mesh(index) => f.write_str(&format!("Mesh{index}")),
|
||||||
|
FbxAssetLabel::Material(index) => f.write_str(&format!("Material{index}")),
|
||||||
|
FbxAssetLabel::Animation(index) => f.write_str(&format!("Animation{index}")),
|
||||||
|
FbxAssetLabel::AnimationStack(index) => f.write_str(&format!("AnimationStack{index}")),
|
||||||
|
FbxAssetLabel::Skeleton(index) => f.write_str(&format!("Skeleton{index}")),
|
||||||
|
FbxAssetLabel::Node(index) => f.write_str(&format!("Node{index}")),
|
||||||
|
FbxAssetLabel::Skin(index) => f.write_str(&format!("Skin{index}")),
|
||||||
|
FbxAssetLabel::Light(index) => f.write_str(&format!("Light{index}")),
|
||||||
|
FbxAssetLabel::Camera(index) => f.write_str(&format!("Camera{index}")),
|
||||||
|
FbxAssetLabel::Texture(index) => f.write_str(&format!("Texture{index}")),
|
||||||
|
FbxAssetLabel::DefaultScene => f.write_str("DefaultScene"),
|
||||||
|
FbxAssetLabel::DefaultMaterial => f.write_str("DefaultMaterial"),
|
||||||
|
FbxAssetLabel::RootNode => f.write_str("RootNode"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FbxAssetLabel {
|
||||||
|
/// Add this label to an asset path
|
||||||
|
pub fn from_asset(&self, path: impl Into<AssetPath<'static>>) -> AssetPath<'static> {
|
||||||
|
path.into().with_label(self.to_string())
|
||||||
|
}
|
||||||
|
}
|
2034
crates/bevy_fbx/src/lib.rs
Normal file
2034
crates/bevy_fbx/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -195,6 +195,7 @@ bevy_core_pipeline = ["dep:bevy_core_pipeline", "bevy_image"]
|
|||||||
bevy_anti_aliasing = ["dep:bevy_anti_aliasing", "bevy_image"]
|
bevy_anti_aliasing = ["dep:bevy_anti_aliasing", "bevy_image"]
|
||||||
bevy_gizmos = ["dep:bevy_gizmos", "bevy_image"]
|
bevy_gizmos = ["dep:bevy_gizmos", "bevy_image"]
|
||||||
bevy_gltf = ["dep:bevy_gltf", "bevy_image"]
|
bevy_gltf = ["dep:bevy_gltf", "bevy_image"]
|
||||||
|
bevy_fbx = ["dep:bevy_fbx", "bevy_image", "bevy_animation"]
|
||||||
bevy_ui = ["dep:bevy_ui", "bevy_image"]
|
bevy_ui = ["dep:bevy_ui", "bevy_image"]
|
||||||
bevy_ui_render = ["dep:bevy_ui_render"]
|
bevy_ui_render = ["dep:bevy_ui_render"]
|
||||||
bevy_image = ["dep:bevy_image"]
|
bevy_image = ["dep:bevy_image"]
|
||||||
@ -430,6 +431,7 @@ bevy_dev_tools = { path = "../bevy_dev_tools", optional = true, version = "0.17.
|
|||||||
bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.17.0-dev" }
|
bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.17.0-dev" }
|
||||||
bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.17.0-dev", default-features = false }
|
bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.17.0-dev", default-features = false }
|
||||||
bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.17.0-dev" }
|
bevy_gltf = { path = "../bevy_gltf", optional = true, version = "0.17.0-dev" }
|
||||||
|
bevy_fbx = { path = "../bevy_fbx", optional = true, version = "0.17.0-dev" }
|
||||||
bevy_feathers = { path = "../bevy_feathers", optional = true, version = "0.17.0-dev" }
|
bevy_feathers = { path = "../bevy_feathers", optional = true, version = "0.17.0-dev" }
|
||||||
bevy_image = { path = "../bevy_image", optional = true, version = "0.17.0-dev" }
|
bevy_image = { path = "../bevy_image", optional = true, version = "0.17.0-dev" }
|
||||||
bevy_mesh = { path = "../bevy_mesh", optional = true, version = "0.17.0-dev" }
|
bevy_mesh = { path = "../bevy_mesh", optional = true, version = "0.17.0-dev" }
|
||||||
|
@ -54,6 +54,8 @@ plugin_group! {
|
|||||||
// compressed texture formats.
|
// compressed texture formats.
|
||||||
#[cfg(feature = "bevy_gltf")]
|
#[cfg(feature = "bevy_gltf")]
|
||||||
bevy_gltf:::GltfPlugin,
|
bevy_gltf:::GltfPlugin,
|
||||||
|
#[cfg(feature = "bevy_fbx")]
|
||||||
|
bevy_fbx:::FbxPlugin,
|
||||||
#[cfg(feature = "bevy_audio")]
|
#[cfg(feature = "bevy_audio")]
|
||||||
bevy_audio:::AudioPlugin,
|
bevy_audio:::AudioPlugin,
|
||||||
#[cfg(feature = "bevy_gilrs")]
|
#[cfg(feature = "bevy_gilrs")]
|
||||||
|
@ -37,6 +37,8 @@ pub use bevy_core_widgets as core_widgets;
|
|||||||
pub use bevy_dev_tools as dev_tools;
|
pub use bevy_dev_tools as dev_tools;
|
||||||
pub use bevy_diagnostic as diagnostic;
|
pub use bevy_diagnostic as diagnostic;
|
||||||
pub use bevy_ecs as ecs;
|
pub use bevy_ecs as ecs;
|
||||||
|
#[cfg(feature = "bevy_fbx")]
|
||||||
|
pub use bevy_fbx as fbx;
|
||||||
#[cfg(feature = "bevy_feathers")]
|
#[cfg(feature = "bevy_feathers")]
|
||||||
pub use bevy_feathers as feathers;
|
pub use bevy_feathers as feathers;
|
||||||
#[cfg(feature = "bevy_gilrs")]
|
#[cfg(feature = "bevy_gilrs")]
|
||||||
|
@ -83,6 +83,10 @@ pub use crate::state::prelude::*;
|
|||||||
#[cfg(feature = "bevy_gltf")]
|
#[cfg(feature = "bevy_gltf")]
|
||||||
pub use crate::gltf::prelude::*;
|
pub use crate::gltf::prelude::*;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(feature = "bevy_fbx")]
|
||||||
|
pub use crate::fbx::prelude::*;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(feature = "bevy_picking")]
|
#[cfg(feature = "bevy_picking")]
|
||||||
pub use crate::picking::prelude::*;
|
pub use crate::picking::prelude::*;
|
||||||
|
76
examples/3d/load_fbx.rs
Normal file
76
examples/3d/load_fbx.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//! This example demonstrates how to load FBX files using the `bevy_fbx` crate.
|
||||||
|
//!
|
||||||
|
//! The example loads a simple cube model from an FBX file and displays it
|
||||||
|
//! with proper lighting and shadows. The cube should rotate in the scene.
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
fbx::FbxAssetLabel,
|
||||||
|
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
use std::f32::consts::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.insert_resource(DirectionalLightShadowMap { size: 4096 })
|
||||||
|
.add_plugins(DefaultPlugins)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, animate_light_direction)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||||
|
commands.spawn((
|
||||||
|
Camera3d::default(),
|
||||||
|
// Transform::from_xyz(0.7, 2.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
EnvironmentMapLight {
|
||||||
|
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||||
|
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||||
|
intensity: 550.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
commands.spawn((
|
||||||
|
DirectionalLight {
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
// This is a relatively small scene, so use tighter shadow
|
||||||
|
// cascade bounds than the default for better quality.
|
||||||
|
// We also adjusted the shadow map to be larger since we're
|
||||||
|
// only using a single cascade.
|
||||||
|
CascadeShadowConfigBuilder {
|
||||||
|
num_cascades: 1,
|
||||||
|
maximum_distance: 1.6,
|
||||||
|
..default()
|
||||||
|
}
|
||||||
|
.build(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Load the FBX file and spawn its first scene
|
||||||
|
commands.spawn(SceneRoot(
|
||||||
|
asset_server.load(FbxAssetLabel::Scene(0).from_asset("models/cube/cube.fbx")),
|
||||||
|
));
|
||||||
|
// commands.spawn(SceneRoot(
|
||||||
|
// asset_server.load(FbxAssetLabel::Scene(0).from_asset("models/nurbs_saddle.fbx")),
|
||||||
|
// ));
|
||||||
|
// commands.spawn(SceneRoot(
|
||||||
|
// asset_server.load(FbxAssetLabel::Scene(0).from_asset("models/cube_anim.fbx")),
|
||||||
|
// ));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn animate_light_direction(
|
||||||
|
time: Res<Time>,
|
||||||
|
mut query: Query<&mut Transform, With<DirectionalLight>>,
|
||||||
|
) {
|
||||||
|
for mut transform in &mut query {
|
||||||
|
transform.rotation = Quat::from_euler(
|
||||||
|
EulerRot::ZYX,
|
||||||
|
0.0,
|
||||||
|
time.elapsed_secs() * PI / 5.0,
|
||||||
|
-FRAC_PI_4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -164,8 +164,10 @@ Example | Description
|
|||||||
[Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene
|
[Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene
|
||||||
[Lightmaps](../examples/3d/lightmaps.rs) | Rendering a scene with baked lightmaps
|
[Lightmaps](../examples/3d/lightmaps.rs) | Rendering a scene with baked lightmaps
|
||||||
[Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines
|
[Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines
|
||||||
|
[Load FBX](../examples/3d/load_fbx.rs) | Loads and renders an FBX file as a scene
|
||||||
[Load glTF](../examples/3d/load_gltf.rs) | Loads and renders a glTF file as a scene
|
[Load glTF](../examples/3d/load_gltf.rs) | Loads and renders a glTF file as a scene
|
||||||
[Load glTF extras](../examples/3d/load_gltf_extras.rs) | Loads and renders a glTF file as a scene, including the gltf extras
|
[Load glTF extras](../examples/3d/load_gltf_extras.rs) | Loads and renders a glTF file as a scene, including the gltf extras
|
||||||
|
[Load FBX](../examples/3d/load_fbx.rs) | Loads and renders an FBX file as a scene
|
||||||
[Manual Material Implementation](../examples/3d/manual_material.rs) | Demonstrates how to implement a material manually using the mid-level render APIs
|
[Manual Material Implementation](../examples/3d/manual_material.rs) | Demonstrates how to implement a material manually using the mid-level render APIs
|
||||||
[Mesh Ray Cast](../examples/3d/mesh_ray_cast.rs) | Demonstrates ray casting with the `MeshRayCast` system parameter
|
[Mesh Ray Cast](../examples/3d/mesh_ray_cast.rs) | Demonstrates ray casting with the `MeshRayCast` system parameter
|
||||||
[Meshlet](../examples/3d/meshlet.rs) | Meshlet rendering for dense high-poly scenes (experimental)
|
[Meshlet](../examples/3d/meshlet.rs) | Meshlet rendering for dense high-poly scenes (experimental)
|
||||||
@ -529,6 +531,7 @@ Example | Description
|
|||||||
|
|
||||||
Example | Description
|
Example | Description
|
||||||
--- | ---
|
--- | ---
|
||||||
|
[FBX Scene Viewer](../examples/tools/scene_viewer_fbx/main.rs) | A simple way to view FBX models with Bevy. Just run `cargo run --release --example scene_viewer_fbx --features=fbx /path/to/model.fbx`, replacing the path as appropriate. Provides enhanced controls for FBX-specific features like material inspection and texture debugging
|
||||||
[Gamepad Viewer](../examples/tools/gamepad_viewer.rs) | Shows a visualization of gamepad buttons, sticks, and triggers
|
[Gamepad Viewer](../examples/tools/gamepad_viewer.rs) | Shows a visualization of gamepad buttons, sticks, and triggers
|
||||||
[Scene Viewer](../examples/tools/scene_viewer/main.rs) | A simple way to view glTF models with Bevy. Just run `cargo run --release --example scene_viewer /path/to/model.gltf#Scene0`, replacing the path as appropriate. With no arguments it will load the FieldHelmet glTF model from the repository assets subdirectory
|
[Scene Viewer](../examples/tools/scene_viewer/main.rs) | A simple way to view glTF models with Bevy. Just run `cargo run --release --example scene_viewer /path/to/model.gltf#Scene0`, replacing the path as appropriate. With no arguments it will load the FieldHelmet glTF model from the repository assets subdirectory
|
||||||
|
|
||||||
|
164
examples/tools/scene_viewer_fbx/README.md
Normal file
164
examples/tools/scene_viewer_fbx/README.md
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
# FBX Scene Viewer
|
||||||
|
|
||||||
|
A comprehensive FBX scene viewer built with Bevy, designed specifically for viewing and inspecting FBX 3D models with enhanced debugging capabilities.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Complete FBX Support**: Load and view FBX files with meshes, materials, textures, and node hierarchies
|
||||||
|
- **Enhanced Material Inspection**: Detailed material property display and texture debugging
|
||||||
|
- **Interactive Controls**: Camera movement, lighting controls, and scene navigation
|
||||||
|
- **Real-time Information**: Live FBX asset statistics and material information
|
||||||
|
- **Professional Debugging**: Bounding box visualization and material debug modes
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View a specific FBX file
|
||||||
|
cargo run --release --example scene_viewer_fbx --features="fbx" path/to/your/model.fbx
|
||||||
|
|
||||||
|
# View the default cube model
|
||||||
|
cargo run --release --example scene_viewer_fbx --features="fbx"
|
||||||
|
|
||||||
|
# With additional rendering options
|
||||||
|
cargo run --release --example scene_viewer_fbx --features="fbx" --depth-prepass --deferred path/to/model.fbx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Command Line Options
|
||||||
|
|
||||||
|
- `--depth-prepass`: Enable depth prepass for better performance
|
||||||
|
- `--occlusion-culling`: Enable occlusion culling (requires depth prepass)
|
||||||
|
- `--deferred`: Enable deferred shading
|
||||||
|
- `--add-light`: Force spawn a directional light even if the scene has lights
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
### Camera Controls
|
||||||
|
- **WASD**: Move camera
|
||||||
|
- **Mouse**: Look around
|
||||||
|
- **Shift**: Run (faster movement)
|
||||||
|
- **C**: Cycle through cameras (scene cameras + controller camera)
|
||||||
|
|
||||||
|
### Lighting Controls
|
||||||
|
- **L**: Toggle light animation (rotate directional light)
|
||||||
|
- **U**: Toggle shadows on/off
|
||||||
|
|
||||||
|
### Debug Controls
|
||||||
|
- **B**: Toggle bounding box visualization
|
||||||
|
- **M**: Toggle material debug information display
|
||||||
|
- **I**: Print detailed FBX asset information to console
|
||||||
|
|
||||||
|
### Material Debug Information
|
||||||
|
|
||||||
|
When pressing **I**, the viewer will display:
|
||||||
|
- Number of meshes, materials, nodes, skins, and animations
|
||||||
|
- Detailed material properties (base color, metallic, roughness)
|
||||||
|
- Texture information (which textures are applied)
|
||||||
|
- Scene statistics
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
```
|
||||||
|
=== FBX Asset Information ===
|
||||||
|
Meshes: 17
|
||||||
|
Materials: 5
|
||||||
|
Nodes: 22
|
||||||
|
Skins: 0
|
||||||
|
Animation clips: 0
|
||||||
|
|
||||||
|
=== Material Details ===
|
||||||
|
Material 0: base_color=Srgba(red: 0.8, green: 0.8, blue: 0.8, alpha: 1.0), metallic=0, roughness=0.5
|
||||||
|
- Has base color texture
|
||||||
|
- Has normal map
|
||||||
|
- Has metallic/roughness texture
|
||||||
|
|
||||||
|
=== Scene Statistics ===
|
||||||
|
Total mesh entities: 17
|
||||||
|
Total material entities: 17
|
||||||
|
```
|
||||||
|
|
||||||
|
## FBX Features Supported
|
||||||
|
|
||||||
|
### ✅ Fully Supported
|
||||||
|
- **Mesh Geometry**: Vertices, normals, UVs, indices
|
||||||
|
- **Materials**: PBR properties with automatic texture application
|
||||||
|
- **Textures**: All common texture types automatically mapped to StandardMaterial
|
||||||
|
- **Node Hierarchy**: Complete scene graph with transforms
|
||||||
|
- **Skinning Data**: Joint weights and inverse bind matrices
|
||||||
|
|
||||||
|
### 🔧 Enhanced Features
|
||||||
|
- **Automatic Texture Mapping**: FBX textures automatically applied to Bevy materials
|
||||||
|
- **Material Property Extraction**: Base color, metallic, roughness, emission
|
||||||
|
- **Real-time Debugging**: Live material and texture information
|
||||||
|
- **Professional Inspection**: Detailed asset statistics and debugging tools
|
||||||
|
|
||||||
|
### ⚠️ Framework Ready
|
||||||
|
- **Animations**: Complete framework in place, temporarily disabled
|
||||||
|
|
||||||
|
## Comparison with GLTF Scene Viewer
|
||||||
|
|
||||||
|
| Feature | GLTF Scene Viewer | FBX Scene Viewer |
|
||||||
|
|---------|-------------------|------------------|
|
||||||
|
| File Format | GLTF/GLB | FBX |
|
||||||
|
| Material Inspection | Basic | Enhanced with debug info |
|
||||||
|
| Texture Debugging | Limited | Comprehensive |
|
||||||
|
| Asset Information | Scene-based | Detailed FBX-specific |
|
||||||
|
| Animation Support | Full | Framework ready |
|
||||||
|
| Real-time Debug | Basic | Professional-grade |
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
- Built on Bevy's asset system with FBX-specific enhancements
|
||||||
|
- Uses ufbx library for robust FBX parsing
|
||||||
|
- Automatic texture-to-material mapping
|
||||||
|
- Professional debugging and inspection tools
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Efficient mesh loading and rendering
|
||||||
|
- Optional depth prepass and occlusion culling
|
||||||
|
- Deferred shading support for complex scenes
|
||||||
|
- Real-time material property inspection
|
||||||
|
|
||||||
|
### Debugging Capabilities
|
||||||
|
- Live FBX asset statistics
|
||||||
|
- Material property inspection
|
||||||
|
- Texture mapping verification
|
||||||
|
- Bounding box visualization
|
||||||
|
- Scene hierarchy analysis
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Viewing Different FBX Files
|
||||||
|
```bash
|
||||||
|
# Simple cube model
|
||||||
|
cargo run --example scene_viewer_fbx --features="fbx" assets/models/cube/cube.fbx
|
||||||
|
|
||||||
|
# Animated model
|
||||||
|
cargo run --example scene_viewer_fbx --features="fbx" assets/models/cube_anim.fbx
|
||||||
|
|
||||||
|
# Complex scene with materials
|
||||||
|
cargo run --example scene_viewer_fbx --features="fbx" assets/models/instanced_materials.fbx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Testing
|
||||||
|
```bash
|
||||||
|
# High-performance mode with all optimizations
|
||||||
|
cargo run --release --example scene_viewer_fbx --features="fbx" --depth-prepass --occlusion-culling --deferred large_model.fbx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
1. **FBX file not loading**: Ensure the file path is correct and the FBX file is valid
|
||||||
|
2. **Missing textures**: Check that texture files are in the same directory or use absolute paths
|
||||||
|
3. **Performance issues**: Try enabling `--depth-prepass` and `--deferred` for complex scenes
|
||||||
|
|
||||||
|
### Debug Information
|
||||||
|
Use the **I** key to get detailed information about the loaded FBX file, including:
|
||||||
|
- Asset counts and statistics
|
||||||
|
- Material properties and texture mappings
|
||||||
|
- Scene hierarchy information
|
||||||
|
- Performance metrics
|
||||||
|
|
||||||
|
This tool is perfect for FBX asset inspection, material debugging, and scene analysis in Bevy applications.
|
300
examples/tools/scene_viewer_fbx/fbx_viewer_plugin.rs
Normal file
300
examples/tools/scene_viewer_fbx/fbx_viewer_plugin.rs
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
//! An FBX scene viewer plugin. Provides controls for directional lighting, material inspection, and scene navigation.
|
||||||
|
//! To use in your own application:
|
||||||
|
//! - Copy the code for the `FbxViewerPlugin` and add the plugin to your App.
|
||||||
|
//! - Insert an initialized `FbxSceneHandle` resource into your App's `AssetServer`.
|
||||||
|
|
||||||
|
use bevy::{fbx::Fbx, input::common_conditions::input_just_pressed, prelude::*, scene::InstanceId};
|
||||||
|
|
||||||
|
use std::{f32::consts::*, fmt};
|
||||||
|
|
||||||
|
use super::camera_controller::*;
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct FbxSceneHandle {
|
||||||
|
pub fbx_handle: Handle<Fbx>,
|
||||||
|
instance_id: Option<InstanceId>,
|
||||||
|
pub is_loaded: bool,
|
||||||
|
pub has_light: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FbxSceneHandle {
|
||||||
|
pub fn new(fbx_handle: Handle<Fbx>) -> Self {
|
||||||
|
Self {
|
||||||
|
fbx_handle,
|
||||||
|
instance_id: None,
|
||||||
|
is_loaded: false,
|
||||||
|
has_light: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const INSTRUCTIONS: &str = r#"
|
||||||
|
FBX Scene Controls:
|
||||||
|
L - animate light direction
|
||||||
|
U - toggle shadows
|
||||||
|
B - toggle bounding boxes
|
||||||
|
C - cycle through the camera controller and any cameras loaded from the scene
|
||||||
|
M - toggle material debug info
|
||||||
|
I - print FBX asset information
|
||||||
|
"#;
|
||||||
|
|
||||||
|
impl fmt::Display for FbxSceneHandle {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{INSTRUCTIONS}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FbxViewerPlugin;
|
||||||
|
|
||||||
|
impl Plugin for FbxViewerPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.init_resource::<CameraTracker>()
|
||||||
|
.init_resource::<MaterialDebugInfo>()
|
||||||
|
.add_systems(PreUpdate, fbx_load_check)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(
|
||||||
|
update_lights,
|
||||||
|
camera_tracker,
|
||||||
|
toggle_bounding_boxes.run_if(input_just_pressed(KeyCode::KeyB)),
|
||||||
|
toggle_material_debug.run_if(input_just_pressed(KeyCode::KeyM)),
|
||||||
|
print_fbx_info.run_if(input_just_pressed(KeyCode::KeyI)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
struct MaterialDebugInfo {
|
||||||
|
enabled: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_bounding_boxes(mut config: ResMut<GizmoConfigStore>) {
|
||||||
|
config.config_mut::<AabbGizmoConfigGroup>().1.draw_all ^= true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_material_debug(mut debug_info: ResMut<MaterialDebugInfo>) {
|
||||||
|
debug_info.enabled = !debug_info.enabled;
|
||||||
|
if debug_info.enabled {
|
||||||
|
info!("Material debug info enabled - press M again to disable");
|
||||||
|
} else {
|
||||||
|
info!("Material debug info disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_fbx_info(
|
||||||
|
fbx_assets: Res<Assets<Fbx>>,
|
||||||
|
scene_handle: Res<FbxSceneHandle>,
|
||||||
|
materials: Query<&MeshMaterial3d<StandardMaterial>>,
|
||||||
|
meshes: Query<&Mesh3d>,
|
||||||
|
standard_materials: Res<Assets<StandardMaterial>>,
|
||||||
|
) {
|
||||||
|
if let Some(fbx) = fbx_assets.get(&scene_handle.fbx_handle) {
|
||||||
|
info!("=== FBX Asset Information ===");
|
||||||
|
info!("Meshes: {}", fbx.meshes.len());
|
||||||
|
info!("Materials: {}", fbx.materials.len());
|
||||||
|
info!("Nodes: {}", fbx.nodes.len());
|
||||||
|
info!("Skins: {}", fbx.skins.len());
|
||||||
|
info!("Animation clips: {}", fbx.animations.len());
|
||||||
|
|
||||||
|
// Print material information
|
||||||
|
info!("=== Material Details ===");
|
||||||
|
for (i, material_handle) in fbx.materials.iter().enumerate() {
|
||||||
|
if let Some(material) = standard_materials.get(material_handle) {
|
||||||
|
info!(
|
||||||
|
"Material {}: base_color={:?}, metallic={}, roughness={}",
|
||||||
|
i, material.base_color, material.metallic, material.perceptual_roughness
|
||||||
|
);
|
||||||
|
|
||||||
|
if material.base_color_texture.is_some() {
|
||||||
|
info!(" - Has base color texture");
|
||||||
|
}
|
||||||
|
if material.normal_map_texture.is_some() {
|
||||||
|
info!(" - Has normal map");
|
||||||
|
}
|
||||||
|
if material.metallic_roughness_texture.is_some() {
|
||||||
|
info!(" - Has metallic/roughness texture");
|
||||||
|
}
|
||||||
|
if material.emissive_texture.is_some() {
|
||||||
|
info!(" - Has emissive texture");
|
||||||
|
}
|
||||||
|
if material.occlusion_texture.is_some() {
|
||||||
|
info!(" - Has occlusion texture");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("=== Scene Statistics ===");
|
||||||
|
info!("Total mesh entities: {}", meshes.iter().count());
|
||||||
|
info!("Total material entities: {}", materials.iter().count());
|
||||||
|
} else {
|
||||||
|
info!("FBX asset not yet loaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fbx_load_check(
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
mut scenes: ResMut<Assets<Scene>>,
|
||||||
|
fbx_assets: Res<Assets<Fbx>>,
|
||||||
|
mut scene_handle: ResMut<FbxSceneHandle>,
|
||||||
|
mut scene_spawner: ResMut<SceneSpawner>,
|
||||||
|
) {
|
||||||
|
match scene_handle.instance_id {
|
||||||
|
None => {
|
||||||
|
if asset_server
|
||||||
|
.load_state(&scene_handle.fbx_handle)
|
||||||
|
.is_loaded()
|
||||||
|
{
|
||||||
|
let fbx = fbx_assets.get(&scene_handle.fbx_handle).unwrap();
|
||||||
|
|
||||||
|
info!("FBX loaded successfully!");
|
||||||
|
info!(
|
||||||
|
"Found {} meshes, {} materials, {} nodes",
|
||||||
|
fbx.meshes.len(),
|
||||||
|
fbx.materials.len(),
|
||||||
|
fbx.nodes.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the FBX scene has lights
|
||||||
|
if let Some(scene_handle_ref) = fbx.scenes.first() {
|
||||||
|
let scene = scenes.get_mut(scene_handle_ref).unwrap();
|
||||||
|
let mut query = scene
|
||||||
|
.world
|
||||||
|
.query::<(Option<&DirectionalLight>, Option<&PointLight>)>();
|
||||||
|
scene_handle.has_light = query.iter(&scene.world).any(
|
||||||
|
|(maybe_directional_light, maybe_point_light)| {
|
||||||
|
maybe_directional_light.is_some() || maybe_point_light.is_some()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
scene_handle.instance_id =
|
||||||
|
Some(scene_spawner.spawn(scene_handle_ref.clone_weak()));
|
||||||
|
|
||||||
|
info!("Spawning FBX scene...");
|
||||||
|
} else {
|
||||||
|
warn!("FBX file contains no scenes!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(instance_id) if !scene_handle.is_loaded => {
|
||||||
|
if scene_spawner.instance_is_ready(instance_id) {
|
||||||
|
info!("FBX scene loaded and ready!");
|
||||||
|
scene_handle.is_loaded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_lights(
|
||||||
|
key_input: Res<ButtonInput<KeyCode>>,
|
||||||
|
time: Res<Time>,
|
||||||
|
mut query: Query<(&mut Transform, &mut DirectionalLight)>,
|
||||||
|
mut animate_directional_light: Local<bool>,
|
||||||
|
) {
|
||||||
|
for (_, mut light) in &mut query {
|
||||||
|
if key_input.just_pressed(KeyCode::KeyU) {
|
||||||
|
light.shadows_enabled = !light.shadows_enabled;
|
||||||
|
info!(
|
||||||
|
"Shadows {}",
|
||||||
|
if light.shadows_enabled {
|
||||||
|
"enabled"
|
||||||
|
} else {
|
||||||
|
"disabled"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if key_input.just_pressed(KeyCode::KeyL) {
|
||||||
|
*animate_directional_light = !*animate_directional_light;
|
||||||
|
info!(
|
||||||
|
"Light animation {}",
|
||||||
|
if *animate_directional_light {
|
||||||
|
"enabled"
|
||||||
|
} else {
|
||||||
|
"disabled"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if *animate_directional_light {
|
||||||
|
for (mut transform, _) in &mut query {
|
||||||
|
transform.rotation = Quat::from_euler(
|
||||||
|
EulerRot::ZYX,
|
||||||
|
0.0,
|
||||||
|
time.elapsed_secs() * PI / 15.0,
|
||||||
|
-FRAC_PI_4,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Default)]
|
||||||
|
struct CameraTracker {
|
||||||
|
active_index: Option<usize>,
|
||||||
|
cameras: Vec<Entity>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CameraTracker {
|
||||||
|
fn track_camera(&mut self, entity: Entity) -> bool {
|
||||||
|
self.cameras.push(entity);
|
||||||
|
if self.active_index.is_none() {
|
||||||
|
self.active_index = Some(self.cameras.len() - 1);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_camera(&self) -> Option<Entity> {
|
||||||
|
self.active_index.map(|i| self.cameras[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_next_active(&mut self) -> Option<Entity> {
|
||||||
|
let active_index = self.active_index?;
|
||||||
|
let new_i = (active_index + 1) % self.cameras.len();
|
||||||
|
self.active_index = Some(new_i);
|
||||||
|
Some(self.cameras[new_i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn camera_tracker(
|
||||||
|
mut camera_tracker: ResMut<CameraTracker>,
|
||||||
|
keyboard_input: Res<ButtonInput<KeyCode>>,
|
||||||
|
mut queries: ParamSet<(
|
||||||
|
Query<(Entity, &mut Camera), (Added<Camera>, Without<CameraController>)>,
|
||||||
|
Query<(Entity, &mut Camera), (Added<Camera>, With<CameraController>)>,
|
||||||
|
Query<&mut Camera>,
|
||||||
|
)>,
|
||||||
|
) {
|
||||||
|
// track added scene camera entities first, to ensure they are preferred for the
|
||||||
|
// default active camera
|
||||||
|
for (entity, mut camera) in queries.p0().iter_mut() {
|
||||||
|
camera.is_active = camera_tracker.track_camera(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate added custom camera entities second
|
||||||
|
for (entity, mut camera) in queries.p1().iter_mut() {
|
||||||
|
camera.is_active = camera_tracker.track_camera(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyboard_input.just_pressed(KeyCode::KeyC) {
|
||||||
|
// disable currently active camera
|
||||||
|
if let Some(e) = camera_tracker.active_camera() {
|
||||||
|
if let Ok(mut camera) = queries.p2().get_mut(e) {
|
||||||
|
camera.is_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// enable next active camera
|
||||||
|
if let Some(e) = camera_tracker.set_next_active() {
|
||||||
|
if let Ok(mut camera) = queries.p2().get_mut(e) {
|
||||||
|
camera.is_active = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
"Switched to camera {}",
|
||||||
|
camera_tracker.active_index.unwrap_or(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
198
examples/tools/scene_viewer_fbx/main.rs
Normal file
198
examples/tools/scene_viewer_fbx/main.rs
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
//! A simple FBX scene viewer made with Bevy.
|
||||||
|
//!
|
||||||
|
//! Just run `cargo run --release --example scene_viewer_fbx --features="fbx" /path/to/model.fbx`,
|
||||||
|
//! replacing the path as appropriate.
|
||||||
|
//! With no arguments it will load a default FBX model if available.
|
||||||
|
//! Pass `--help` to see all the supported arguments.
|
||||||
|
//!
|
||||||
|
//! If you want to hot reload asset changes, enable the `file_watcher` cargo feature.
|
||||||
|
|
||||||
|
use argh::FromArgs;
|
||||||
|
use bevy::{
|
||||||
|
asset::UnapprovedPathMode,
|
||||||
|
core_pipeline::prepass::{DeferredPrepass, DepthPrepass},
|
||||||
|
pbr::DefaultOpaqueRendererMethod,
|
||||||
|
prelude::*,
|
||||||
|
render::{
|
||||||
|
experimental::occlusion_culling::OcclusionCulling,
|
||||||
|
primitives::{Aabb, Sphere},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[path = "../../helpers/camera_controller.rs"]
|
||||||
|
mod camera_controller;
|
||||||
|
|
||||||
|
mod fbx_viewer_plugin;
|
||||||
|
|
||||||
|
use camera_controller::{CameraController, CameraControllerPlugin};
|
||||||
|
use fbx_viewer_plugin::{FbxSceneHandle, FbxViewerPlugin};
|
||||||
|
|
||||||
|
/// A simple FBX scene viewer made with Bevy
|
||||||
|
#[derive(FromArgs, Resource)]
|
||||||
|
struct Args {
|
||||||
|
/// the path to the FBX scene
|
||||||
|
#[argh(positional, default = "\"assets/models/cube/cube.fbx\".to_string()")]
|
||||||
|
scene_path: String,
|
||||||
|
/// enable a depth prepass
|
||||||
|
#[argh(switch)]
|
||||||
|
depth_prepass: Option<bool>,
|
||||||
|
/// enable occlusion culling
|
||||||
|
#[argh(switch)]
|
||||||
|
occlusion_culling: Option<bool>,
|
||||||
|
/// enable deferred shading
|
||||||
|
#[argh(switch)]
|
||||||
|
deferred: Option<bool>,
|
||||||
|
/// spawn a light even if the scene already has one
|
||||||
|
#[argh(switch)]
|
||||||
|
add_light: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
let args: Args = argh::from_env();
|
||||||
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
let args: Args = Args::from_args(&[], &[]).unwrap();
|
||||||
|
|
||||||
|
let deferred = args.deferred;
|
||||||
|
|
||||||
|
let mut app = App::new();
|
||||||
|
app.add_plugins((
|
||||||
|
DefaultPlugins
|
||||||
|
.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: "bevy fbx scene viewer".to_string(),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
})
|
||||||
|
.set(AssetPlugin {
|
||||||
|
file_path: std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()),
|
||||||
|
// Allow scenes to be loaded from anywhere on disk
|
||||||
|
unapproved_path_mode: UnapprovedPathMode::Allow,
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
CameraControllerPlugin,
|
||||||
|
FbxViewerPlugin,
|
||||||
|
))
|
||||||
|
.insert_resource(args)
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(PreUpdate, setup_scene_after_load);
|
||||||
|
|
||||||
|
// If deferred shading was requested, turn it on.
|
||||||
|
if deferred == Some(true) {
|
||||||
|
app.insert_resource(DefaultOpaqueRendererMethod::deferred());
|
||||||
|
}
|
||||||
|
|
||||||
|
app.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, args: Res<Args>) {
|
||||||
|
let scene_path = &args.scene_path;
|
||||||
|
info!("Loading FBX file: {}", scene_path);
|
||||||
|
|
||||||
|
commands.insert_resource(FbxSceneHandle::new(asset_server.load(scene_path.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_scene_after_load(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut setup: Local<bool>,
|
||||||
|
mut scene_handle: ResMut<FbxSceneHandle>,
|
||||||
|
asset_server: Res<AssetServer>,
|
||||||
|
args: Res<Args>,
|
||||||
|
meshes: Query<(&GlobalTransform, Option<&Aabb>), With<Mesh3d>>,
|
||||||
|
) {
|
||||||
|
if scene_handle.is_loaded && !*setup {
|
||||||
|
*setup = true;
|
||||||
|
// Find an approximate bounding box of the scene from its meshes
|
||||||
|
if meshes.iter().any(|(_, maybe_aabb)| maybe_aabb.is_none()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut min = Vec3A::splat(f32::MAX);
|
||||||
|
let mut max = Vec3A::splat(f32::MIN);
|
||||||
|
for (transform, maybe_aabb) in &meshes {
|
||||||
|
let aabb = maybe_aabb.unwrap();
|
||||||
|
// If the Aabb had not been rotated, applying the non-uniform scale would produce the
|
||||||
|
// correct bounds. However, it could very well be rotated and so we first convert to
|
||||||
|
// a Sphere, and then back to an Aabb to find the conservative min and max points.
|
||||||
|
let sphere = Sphere {
|
||||||
|
center: Vec3A::from(transform.transform_point(Vec3::from(aabb.center))),
|
||||||
|
radius: transform.radius_vec3a(aabb.half_extents),
|
||||||
|
};
|
||||||
|
let aabb = Aabb::from(sphere);
|
||||||
|
min = min.min(aabb.min());
|
||||||
|
max = max.max(aabb.max());
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = (max - min).length();
|
||||||
|
let aabb = Aabb::from_min_max(Vec3::from(min), Vec3::from(max));
|
||||||
|
|
||||||
|
info!("Spawning a controllable 3D perspective camera");
|
||||||
|
let mut projection = PerspectiveProjection::default();
|
||||||
|
projection.far = projection.far.max(size * 10.0);
|
||||||
|
|
||||||
|
let walk_speed = size * 3.0;
|
||||||
|
let camera_controller = CameraController {
|
||||||
|
walk_speed,
|
||||||
|
run_speed: 3.0 * walk_speed,
|
||||||
|
..default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Display the controls of the scene viewer
|
||||||
|
info!("{}", camera_controller);
|
||||||
|
info!("{}", *scene_handle);
|
||||||
|
|
||||||
|
let mut camera = commands.spawn((
|
||||||
|
Camera3d::default(),
|
||||||
|
Projection::from(projection),
|
||||||
|
Transform::from_translation(Vec3::from(aabb.center) + size * Vec3::new(0.5, 0.25, 0.5))
|
||||||
|
.looking_at(Vec3::from(aabb.center), Vec3::Y),
|
||||||
|
Camera {
|
||||||
|
is_active: false,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
EnvironmentMapLight {
|
||||||
|
diffuse_map: asset_server
|
||||||
|
.load("assets/environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||||
|
specular_map: asset_server
|
||||||
|
.load("assets/environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||||
|
intensity: 150.0,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
camera_controller,
|
||||||
|
));
|
||||||
|
|
||||||
|
// If occlusion culling was requested, include the relevant components.
|
||||||
|
// The Z-prepass is currently required.
|
||||||
|
if args.occlusion_culling == Some(true) {
|
||||||
|
camera.insert((DepthPrepass, OcclusionCulling));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the depth prepass was requested, include it.
|
||||||
|
if args.depth_prepass == Some(true) {
|
||||||
|
camera.insert(DepthPrepass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If deferred shading was requested, include the prepass.
|
||||||
|
if args.deferred == Some(true) {
|
||||||
|
camera
|
||||||
|
.insert(Msaa::Off)
|
||||||
|
.insert(DepthPrepass)
|
||||||
|
.insert(DeferredPrepass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spawn a default light if the scene does not have one
|
||||||
|
if !scene_handle.has_light || args.add_light == Some(true) {
|
||||||
|
info!("Spawning a directional light");
|
||||||
|
let mut light = commands.spawn((
|
||||||
|
DirectionalLight::default(),
|
||||||
|
Transform::from_xyz(1.0, 1.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
|
||||||
|
));
|
||||||
|
if args.occlusion_culling == Some(true) {
|
||||||
|
light.insert(OcclusionCulling);
|
||||||
|
}
|
||||||
|
|
||||||
|
scene_handle.has_light = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user