init
This commit is contained in:
@@ -1,4 +1,358 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:animations/utils/binary_manager.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'model_viewer.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// 初始化3D模型资源
|
||||
try {
|
||||
await BinaryManager.initializeAppResources();
|
||||
print('3D模型资源初始化成功');
|
||||
} catch (e) {
|
||||
print('无法初始化3D模型资源: $e');
|
||||
}
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: '3D模型纹理替换',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
home: const ModelViewerWithTextureReplace(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ModelViewerWithTextureReplace extends StatefulWidget {
|
||||
const ModelViewerWithTextureReplace({super.key});
|
||||
|
||||
@override
|
||||
State<ModelViewerWithTextureReplace> createState() =>
|
||||
_ModelViewerWithTextureReplaceState();
|
||||
}
|
||||
|
||||
class _ModelViewerWithTextureReplaceState
|
||||
extends State<ModelViewerWithTextureReplace> {
|
||||
bool _isLoading = true;
|
||||
String? _modelPath;
|
||||
String? _texturePath;
|
||||
int _keyValue = 0;
|
||||
String _statusMessage = "正在加载模型...";
|
||||
bool _isReplacingTexture = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
BinaryManager.readLocalJson();
|
||||
_load3DModels();
|
||||
// _loadModel();
|
||||
}
|
||||
|
||||
// 加载3D模型文件
|
||||
Future<void> _load3DModels() async {
|
||||
try {
|
||||
setState(() {
|
||||
_statusMessage = "获取模型路径...";
|
||||
});
|
||||
|
||||
// 使用BinaryManager获取3D模型文件路径
|
||||
// final supportDir = await getApplicationSupportDirectory();
|
||||
// final cupDir = Directory('${supportDir.path}/cup');
|
||||
// final modelFile = File('${cupDir.path}/model.gltf');
|
||||
|
||||
final modelPath = await BinaryManager.get3DModelPath("model.gltf");
|
||||
|
||||
final texturePath = await BinaryManager.get3DModelPath("texture.jpg");
|
||||
|
||||
// print("路径: $modelPath");
|
||||
// print("文件是否存在: ${await File(modelPath).exists()}");
|
||||
// print("文件路径: ${await File(modelPath).uri}");
|
||||
//
|
||||
// setState(() {
|
||||
// _modelPath = File(modelPath).uri.toString(); // 确保是本地路径
|
||||
// });
|
||||
|
||||
// if (!await File(modelPath).exists()) {
|
||||
// print("❌ 模型文件不存在: $modelPath");
|
||||
// }
|
||||
// if (!await File(texturePath).exists()) {
|
||||
// print("❌ 纹理文件不存在: $texturePath");
|
||||
// }
|
||||
print('modelPath=${modelPath}\ntexturePath=${texturePath}');
|
||||
setState(() {
|
||||
_modelPath = modelPath;
|
||||
_texturePath = texturePath;
|
||||
_isLoading = false;
|
||||
_statusMessage = "加载完成";
|
||||
});
|
||||
} catch (e) {
|
||||
print('加载3D模型失败: $e');
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_statusMessage = "加载失败: $e";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 使用FilePicker选择新纹理
|
||||
Future<void> _pickNewTexture() async {
|
||||
if (_isReplacingTexture || _texturePath == null) return;
|
||||
|
||||
setState(() {
|
||||
_isReplacingTexture = true;
|
||||
_statusMessage = "正在选择纹理文件...";
|
||||
});
|
||||
|
||||
try {
|
||||
// 使用FilePicker选择文件
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.image,
|
||||
allowMultiple: false,
|
||||
);
|
||||
|
||||
if (result != null &&
|
||||
result.files.isNotEmpty &&
|
||||
result.files.single.path != null) {
|
||||
setState(() {
|
||||
_statusMessage = "正在应用新纹理...";
|
||||
});
|
||||
|
||||
// 获取选中的文件
|
||||
PlatformFile file = result.files.first;
|
||||
File newTexture = File(file.path!);
|
||||
|
||||
// 检查文件是否存在
|
||||
if (await newTexture.exists()) {
|
||||
// 先备份旧纹理文件(可选)
|
||||
final backupTexturePath = '${_texturePath!}.backup';
|
||||
final oldTexture = File(_texturePath!);
|
||||
if (await oldTexture.exists()) {
|
||||
await oldTexture.copy(backupTexturePath);
|
||||
}
|
||||
|
||||
// 复制选中的图片到纹理路径
|
||||
await newTexture.copy(_texturePath!);
|
||||
|
||||
// 给渲染器一点时间来处理文件变化
|
||||
await Future.delayed(const Duration(milliseconds: 3000));
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LocalModelViewer(),
|
||||
),
|
||||
);
|
||||
|
||||
// // 强制重建ViewerWidget以应用新纹理
|
||||
// setState(() {
|
||||
// _keyValue++;
|
||||
// _statusMessage = "纹理已更新";
|
||||
// });
|
||||
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// const SnackBar(content: Text('纹理已成功更新')),
|
||||
// );
|
||||
} else {
|
||||
setState(() {
|
||||
_statusMessage = "文件不存在";
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setState(() {
|
||||
_statusMessage = "取消选择";
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print('更换纹理失败: $e');
|
||||
setState(() {
|
||||
_statusMessage = "更换纹理失败: $e";
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('更换纹理失败: $e')),
|
||||
);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isReplacingTexture = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 重置纹理到原始状态
|
||||
Future<void> _resetTexture() async {
|
||||
if (_isReplacingTexture || _texturePath == null) return;
|
||||
|
||||
setState(() {
|
||||
_isReplacingTexture = true;
|
||||
_statusMessage = "正在重置纹理...";
|
||||
});
|
||||
|
||||
try {
|
||||
// 检查备份文件是否存在
|
||||
final backupTexturePath = '${_texturePath!}.backup';
|
||||
final backupFile = File(backupTexturePath);
|
||||
|
||||
if (await backupFile.exists()) {
|
||||
// 恢复备份文件
|
||||
await backupFile.copy(_texturePath!);
|
||||
|
||||
// 给渲染器一点时间来处理文件变化
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// 强制重建ViewerWidget以应用恢复的纹理
|
||||
setState(() {
|
||||
_keyValue++;
|
||||
_statusMessage = "纹理已重置";
|
||||
});
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('纹理已重置到原始状态')),
|
||||
);
|
||||
} else {
|
||||
setState(() {
|
||||
_statusMessage = "没有备份文件可恢复";
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('没有备份文件可恢复')),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
print('重置纹理失败: $e');
|
||||
setState(() {
|
||||
_statusMessage = "重置纹理失败: $e";
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('重置纹理失败: $e')),
|
||||
);
|
||||
} finally {
|
||||
setState(() {
|
||||
_isReplacingTexture = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('3D模型纹理替换'),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.texture),
|
||||
onPressed: _isReplacingTexture ? null : _pickNewTexture,
|
||||
tooltip: '更换纹理',
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _isLoading
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const CircularProgressIndicator(),
|
||||
const SizedBox(height: 16),
|
||||
Text(_statusMessage),
|
||||
],
|
||||
),
|
||||
)
|
||||
: _modelPath == null
|
||||
? Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error, size: 48, color: Colors.red),
|
||||
const SizedBox(height: 16),
|
||||
const Text('加载模型失败'),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: _load3DModels,
|
||||
child: const Text('重试'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
// Expanded(
|
||||
// child: ViewerWidget(
|
||||
// key: ValueKey(_keyValue),
|
||||
// assetPath: "file://$_modelPath",
|
||||
// skyboxPath: "assets/default_env_skybox.ktx",
|
||||
// iblPath: "assets/default_env_ibl.ktx",
|
||||
// transformToUnitCube: true,
|
||||
// initialCameraPosition: Vector3(0, 0, 6),
|
||||
// background: Colors.grey[200],
|
||||
// manipulatorType: ManipulatorType.ORBIT,
|
||||
// initial: const Center(
|
||||
// child: CircularProgressIndicator(),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
color: Colors.grey[100],
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.texture),
|
||||
label: const Text('选择纹理图片'),
|
||||
onPressed: _isReplacingTexture
|
||||
? null
|
||||
: _pickNewTexture,
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('重置纹理'),
|
||||
onPressed:
|
||||
_isReplacingTexture ? null : _resetTexture,
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
icon: const Icon(Icons.camera),
|
||||
label: const Text('重置视图'),
|
||||
onPressed: _isReplacingTexture
|
||||
? null
|
||||
: () {
|
||||
setState(() {
|
||||
_keyValue++;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_statusMessage,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
/*
|
||||
import 'dart:async';
|
||||
import 'package:animations/cup_page.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:flutter/material.dart' hide View;
|
||||
import 'package:thermion_flutter/thermion_flutter.dart';
|
||||
@@ -21,7 +375,7 @@ class MyApp extends StatelessWidget {
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const MyHomePage(title: 'Thermion Demo Home Page'),
|
||||
home: const CupPage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +393,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
ThermionViewer? _thermionViewer;
|
||||
|
||||
ThermionAsset? _asset;
|
||||
final _droneUri = "assets/BusterDrone/scene.gltf";
|
||||
final _droneUri = "assets/cup/model.gltf";
|
||||
final _cubeUri = "assets/cube_with_morph_targets.glb";
|
||||
String? _loaded;
|
||||
|
||||
@@ -68,7 +422,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
gltfAnimations.clear();
|
||||
gltfAnimations.addAll(labels);
|
||||
selectedGltfAnimation = 0;
|
||||
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
@@ -165,3 +518,4 @@ class _MyHomePageState extends State<MyHomePage> {
|
||||
]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user