diff --git a/assets/blender/characters/CMakeLists.txt b/assets/blender/characters/CMakeLists.txt index b48a905..e4a411b 100644 --- a/assets/blender/characters/CMakeLists.txt +++ b/assets/blender/characters/CMakeLists.txt @@ -24,20 +24,21 @@ list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/characters/e list(APPEND CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/${EDITED_BLEND}/normal-${EDITED_BLEND}.glb) endforeach() -add_custom_command( - OUTPUT ${CHARACTER_GLBS} - COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES} - COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py - COMMAND ${CMAKE_COMMAND} -E touch ${CHARACTER_GLBS} - DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) - set(VRM_IMPORTED_BLENDS ${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-female.blend ${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-male.blend ${CMAKE_BINARY_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend ) +add_custom_command( + OUTPUT ${CHARACTER_GLBS} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES} + COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py + COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${CHARACTER_GLBS} + DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + + set(VRM_SOURCE) set(VRM_FILES buch1-chibi.vrm buch1.vrm jane2-dress.vrm jane2.vrm) @@ -58,15 +59,59 @@ foreach(MIXAMO_FILE ${MIXAMO_FILES}) DEPENDS "${INPUT_FILE}" VERBATIM) list(APPEND VRM_SOURCE "${OUTPUT_FILE}") endforeach() -add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS} +add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/blender-addons-installed COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/blender/scripts/addons ${CMAKE_BINARY_DIR}/assets/blender/scripts/addons - COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/install_addons.py - COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES} - COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py - COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${VRM_IMPORTED_BLENDS} - DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py - ${VRM_SOURCE} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/install_addons.py + COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/blender-addons-installed + DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/addons/3.6/VRM_Addon_for_Blender-release.zip + ${CMAKE_SOURCE_DIR}/assets/blender/scripts/addons/3.6/io_ogre.zip + ${CMAKE_SOURCE_DIR}/assets/blender/scripts/install_addons.py +) + +#add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS} +# COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES} +# COMMAND ${BLENDER} -b -Y -P ${CMAKE_CURRENT_SOURCE_DIR}/import_vrm.py +# COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${VRM_IMPORTED_BLENDS} +# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/import_vrm.py +# ${CMAKE_CURRENT_BINARY_DIR}/blender-assons-installed +# ${VRM_SOURCE} +# WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +function(blender_import_vrm BLEND VRM EDITABLE RIG) + get_filename_component(TARGET_NAME ${BLEND} NAME_WE) + get_filename_component(VRM_NAME ${VRM} NAME_WE) + add_custom_command(OUTPUT ${BLEND} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES} + COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm2.py -- ${VRM_NAME}.vrm ${TARGET_NAME}.blend ${EDITABLE} ${RIG} + COMMAND ${CMAKE_COMMAND} -D FILE=${BLEND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake + COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${BLEND} + DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm2.py + ${CMAKE_CURRENT_BINARY_DIR}/blender-addons-installed + ${VRM} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endfunction() + +blender_import_vrm(${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-female.blend + ${CMAKE_BINARY_DIR}/assets/vroid/jane2-dress.vrm + modelling-vroid-normal-female.blend female) +blender_import_vrm(${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-male.blend + ${CMAKE_BINARY_DIR}/assets/vroid/buch1.vrm + modelling-vroid-normal-male.blend male) +blender_import_vrm(${CMAKE_BINARY_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend + ${CMAKE_BINARY_DIR}/assets/vroid/buch1-chibi.vrm + modelling-shape-male-chibi.blend male) + +#add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS} +# COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES} +# COMMAND ${BLENDER} -b -Y -P ${CMAKE_CURRENT_SOURCE_DIR}/import_vrm2.py jane2-dress.vrm vrm-vroid-normal-female.blend modelling-vroid-normal-female.blend female +# COMMAND ${BLENDER} -b -Y -P ${CMAKE_CURRENT_SOURCE_DIR}/import_vrm2.py buch1.vrm vrm-vroid-normal-male.blend modelling-vroid-normal-male.blend male +# COMMAND ${BLENDER} -b -Y -P ${CMAKE_CURRENT_SOURCE_DIR}/import_vrm2.py buch1-chibi.vrm shapes/male/vrm-vroid-normal-male-chibi.blend modelling-shape-male-chibi.blend male +# COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${VRM_IMPORTED_BLENDS} +# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/import_vrm.py +# ${CMAKE_CURRENT_BINARY_DIR}/blender-assons-installed +# ${VRM_SOURCE} +# WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set(COPY_BLENDS edited-shape-test-male.blend edited-normal-male-base.blend) diff --git a/assets/blender/characters/cmake/check_file_size.cmake b/assets/blender/characters/cmake/check_file_size.cmake new file mode 100644 index 0000000..fcdda2f --- /dev/null +++ b/assets/blender/characters/cmake/check_file_size.cmake @@ -0,0 +1,11 @@ +if(EXISTS "${FILE}") + file(SIZE "${FILE}" FILE_SIZE) + if(FILE_SIZE GREATER 0) + message(STATUS "Build-time check: ${FILE} exists and is not empty.") + else() + message(FATAL_ERROR "Build-time check: ${FILE} exists but is empty!") + endif() +else() + message(FATAL_ERROR "Build-time check: ${FILE} does not exist!") +endif() + diff --git a/assets/blender/scripts/import_vrm2.py b/assets/blender/scripts/import_vrm2.py new file mode 100644 index 0000000..f1084f8 --- /dev/null +++ b/assets/blender/scripts/import_vrm2.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python + +import os, sys, time +import bpy +from math import pi +import glob +import shutil +from mathutils import Vector, Matrix +from math import radians, pi + +argv = sys.argv +argv = argv[argv.index("--") + 1:] + +if bpy.app.version[0] == 3: + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/mixamo/3.6") +if bpy.app.version[0] == 4: + sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + "/mixamo/4.3") +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from vrm import rename +if bpy.app.version[0] == 3: + from mixamo import mixamo_rig +from mixamo.lib.armature import * +from settings import VRMDataFemale, VRMDataMale, VRMDataMaleBabyShape, basepath +from geometry import tris2quads + +class mkrig: + ik_arms = True + ik_legs = True + def __init__(self): + mixamo_rig._make_rig(self) + mixamo_rig.remove_temp_objects() + mixamo_rig.clean_scene() +def get_anim(filepath, root_bone_name="Root", hip_bone_name="mixamorig:Hips", remove_prefix=False, name_prefix="mixamorig:", insert_root=False, delete_armatures=False): + old_objs = set(bpy.context.scene.objects) + try: + import_armature(filepath, root_bone_name, hip_bone_name, remove_prefix, name_prefix, insert_root, delete_armatures) + imported_objects = set(bpy.context.scene.objects) - old_objs + if delete_armatures and num_files > 1: + deleteArmature(imported_objects) + except Exception as e: + log.error("[Mixamo Root] ERROR get_all_anims raised %s when processing %s" % (str(e), filepath)) + bpy.context.scene.frame_start = 0 + bpy.ops.object.mode_set(mode='OBJECT') +def create_root_bone(anim_obj): + bpy.context.view_layer.objects.active = anim_obj + bpy.ops.object.mode_set(mode='EDIT') + root_bone = anim_obj.data.edit_bones.new("Root") + root_bone.tail.y = 0.5 + anim_obj.data.edit_bones["mixamorig:Hips"].parent = anim_obj.data.edit_bones["Root"] + bpy.ops.object.mode_set(mode='OBJECT') +def hips_to_root(anim_obj): + for fc in anim_obj.animation_data.action.fcurves: + if "mixamorig:Hips" in fc.data_path: + if "location" in fc.data_path: + print(fc, fc.data_path, fc.array_index) + if fc.array_index in [0, 2]: + fc.data_path = fc.data_path.replace("mixamorig:Hips", "Root") +def hips_to_root(anim_obj): + for fc in anim_obj.animation_data.action.fcurves: + if "mixamorig:Hips" in fc.data_path: + if "location" in fc.data_path: + print(fc, fc.data_path, fc.array_index) + if fc.array_index in [0, 2]: + fc.data_path = fc.data_path.replace("mixamorig:Hips", "Root") +def hips_to_root2(anim_obj): + for fc in anim_obj.animation_data.action.fcurves: + if "Ctrl_Hips" in fc.data_path: + if "location" in fc.data_path: + print(fc, fc.data_path, fc.array_index) + if fc.array_index in [0, 2]: + data_path = fc.data_path[:] + fcr = anim_obj.animation_data.action.fcurves.new(data_path = data_path.replace("Ctrl_Hips", "Root"), index = fc.array_index) + keys = [0] * (2 * len(fc.keyframe_points)) + fcr.keyframe_points.add(len(fc.keyframe_points)) + fc.keyframe_points.foreach_get("co", keys) + fcr.keyframe_points.foreach_set("co", keys) + print("ROOT") + +class ImpData: + path = argv[0] + outfile = argv[1] + editfile = argv[2] + armature_name = argv[3] + mixamo_animation_path = basepath + "/assets/blender/mixamo/" + argv[3] + "/" + mixamo_animations = ["idle", "walking", "running", "jump", "left_turn", "right_turn", "swimming", "treading_water", "sitting", "hanging-climb", "hanging-idle"] + fbx_scale = 1.0 + def __init__(self): + if self.armature_name == "female": + self.fbx_scale = 0.89 + if self.path == "buch1-chibi.vrm": + self.mixamo_animations = [] + +imp = ImpData() + +bpy.ops.wm.read_homefile(use_empty=True) +for o in bpy.data.objects: + bpy.data.objects.remove(o) +print(basepath + "/assets/vroid/" + imp.path) +result = bpy.ops.import_scene.vrm(filepath=(basepath + "/assets/vroid/" + imp.path)) +if result != {"FINISHED"}: + raise Exception(f"Failed to import vrm: {result}") +for o in bpy.data.objects: + if o.type == 'MESH': + tris2quads.tris2quads(o) +armature_count = 0 +for obj in bpy.data.objects: + if (obj.type == "ARMATURE"): + armature_count += 1 +if armature_count > 1: + raise Exception("Bad scene data") +main_armature = None +for obj in bpy.data.objects: + if (obj.type == "ARMATURE"): + main_armature = obj + break +if main_armature == None: + raise Exception("Bad scene data") +print("Renaming...") +main_armature.select_set(True) +bpy.context.view_layer.objects.active = main_armature +rename.rename_bones(main_armature) +main_armature.select_set(False) +main_armature.name = imp.armature_name +main_armature["VRM_IMPORTED_NAME"] = "female" +if main_armature.animation_data is None: + main_armature.animation_data_create() +main_armature.select_set(True) +bpy.context.view_layer.objects.active = main_armature +mkrig() +main_armature.select_set(False) +bpy.context.view_layer.objects.active = None + +for anim in imp.mixamo_animations: + anim_path = imp.mixamo_animation_path + anim + ".fbx" + print("Load BVH..." + anim_path) + old_objs = set(bpy.context.scene.objects) + bpy.ops.import_scene.fbx(filepath=anim_path, global_scale=imp.fbx_scale) + imported_objects = set(bpy.context.scene.objects) - old_objs + for anim_obj in imported_objects: + if anim_obj == main_armature: + continue + if "VRM_IMPORTED_NAME" in anim_obj: + continue + if main_armature == anim_obj or anim_obj.type != "ARMATURE": + continue + if anim_obj.animation_data == None: + print("Bad object: " + anim_obj.name + " " + anim_obj.type) + bpy.data.objects.remove(anim_obj) + continue + if anim_obj.animation_data.action == None: + print("Bad object: " + anim_obj.name + " " + anim_obj.type) + bpy.data.objects.remove(anim_obj) + continue + print("importing: " + anim_obj.animation_data.action.name) + mixamo_rig._import_anim(anim_obj, main_armature, True) + print("===" + main_armature.animation_data.action.name) + main_armature.animation_data.action.name = anim + hips_to_root2(main_armature) + track = main_armature.animation_data.nla_tracks.new() + action = main_armature.animation_data.action + track.name = action.name + track.strips.new(action.name, int(action.frame_range[0]), action) + track.mute = True + track.lock = True + bpy.data.objects.remove(anim_obj) + imported_objects.clear() + imported_objects = set(bpy.context.scene.objects) - old_objs + for anim_obj in imported_objects: + bpy.data.objects.remove(anim_obj) + +main_armature.animation_data.action = bpy.data.actions.new(name="default") +default_action = main_armature.animation_data.action +bpy.context.view_layer.objects.active = main_armature +main_armature.select_set(True) +layers = enable_all_armature_layers() +bpy.ops.object.mode_set(mode='POSE') +for b in main_armature.pose.bones: + b.location = [0,0,0] + b.rotation_euler = [0,0,0] + b.rotation_quaternion = [1,0,0,0] + b.scale = [1,1,1] +bpy.ops.object.mode_set(mode='OBJECT') + +bpy.ops.object.mode_set(mode='POSE') +for b in main_armature.data.bones: + if b.name.find("Ctrl_") < 0 and b.name.find("ctrl_") < 0: + continue + main_armature.pose.bones[b.name].matrix_basis.identity() + main_armature.pose.bones[b.name].keyframe_insert(data_path='location', frame=1) + rotation_mode = main_armature.pose.bones[b.name].rotation_mode + if rotation_mode == "QUATERNION": + main_armature.pose.bones[b.name].keyframe_insert(data_path='rotation_quaternion', frame=1) + elif rotation_mode == "AXIS_ANGLE": + main_armature.pose.bones[b.name].keyframe_insert(data_path='rotation_axis_angle', frame=1) + else: + main_armature.pose.bones[b.name].keyframe_insert(data_path='rotation_euler', frame=1) + main_armature.pose.bones[b.name].keyframe_insert(data_path='scale', frame=1) +bpy.ops.object.mode_set(mode='OBJECT') +track = main_armature.animation_data.nla_tracks.new() +action = main_armature.animation_data.action +track.name = action.name +track.strips.new(action.name, int(action.frame_range[0]), action) +track.mute = True +track.lock = True +restore_armature_layers(layers) +main_armature.animation_data.action = default_action + +main_armature.select_set(False) +bpy.context.view_layer.objects.active = None +bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/" + imp.outfile)) + diff --git a/src/editor/main.cpp b/src/editor/main.cpp index dc948ed..7c2eafc 100644 --- a/src/editor/main.cpp +++ b/src/editor/main.cpp @@ -20,6 +20,8 @@ #include "AppModule.h" #include "EditorGizmoModule.h" #include "EditorInputModule.h" +#include "PhysicsModule.h" +#include "physics.h" #include "sound.h" class App; @@ -588,10 +590,14 @@ public: { } bool switchWindow = false; - void createContent() + JoltPhysicsWrapper *mJolt; + void createContent() { int i; - sky = new SkyBoxRenderer(mEditorNormalScene.getSceneManager()); + mJolt = new JoltPhysicsWrapper( + mEditorNormalScene.getSceneManager(), + mEditorNormalScene.mCameraNode); + sky = new SkyBoxRenderer(mEditorNormalScene.getSceneManager()); bool drawFirst = true; uint8_t renderQueue = drawFirst ? Ogre::RENDER_QUEUE_SKIES_EARLY : @@ -611,8 +617,12 @@ public: mEditorNormalScene.mScnMgr, /*mDynWorld.get(), */ mEditorNormalScene.mCameraNode, mEditorNormalScene.mCamera, getRenderWindow()); - ECS::get().import (); - ECS::get().import (); + ECS::get().import (); + ECS::get().import (); + ECS::get().import (); + ECS::Physics &ph = ECS::get().ensure(); + ph.physics = mJolt; + ECS::modified(); ECS::get().set( { getRenderWindow(), getDisplayDPI() }); ECS::get() diff --git a/src/gamedata/CharacterAIModule.cpp b/src/gamedata/CharacterAIModule.cpp index 693924d..f882edf 100644 --- a/src/gamedata/CharacterAIModule.cpp +++ b/src/gamedata/CharacterAIModule.cpp @@ -593,13 +593,13 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs) } continue; } - std::cout << "NPC: " << plans.first; - std::cout << " Plans: " << plans.second.size(); + // std::cout << "NPC: " << plans.first; + // std::cout << " Plans: " << plans.second.size(); for (const auto &plan : plans.second) { struct PlanExec pexec; if (plan.plan.size() == 0) continue; - std::cout << " Goal: "; + // std::cout << " Goal: "; plan.goal->goal.dump_bits(); for (const auto &action : plan.plan) { ActionExec::PlanExecData data({ @@ -631,9 +631,9 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs) pexec.action_exec .push_back(e); } else { - std::cout - << action->get_name() - << " "; + // std::cout + // << action->get_name() + // << " "; ActionExec *e = OGRE_NEW ActionExecCommon( data, @@ -641,13 +641,13 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs) pexec.action_exec .push_back(e); } - std::cout << action->get_name() - << " "; + // std::cout << action->get_name() + // << " "; } plan_exec[plans.first] = pexec; break; } - std::cout << std::endl; + //std::cout << std::endl; } }); } diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 7c1a581..9c7c27a 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -107,7 +107,6 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, ecs.set({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(), (int)window->getHeight(), false }); ecs.set({ cameraNode, camera, false }); - PhysicsModule::configurePhysics(); ecs.add(); ecs.add(); ecs.observer("Game_Start_Scen_Startup") @@ -212,9 +211,6 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, Ogre::Camera *camera, Ogre::RenderWindow *window) { setup_minimal(); - Physics &ph = ECS::get().ensure(); - ph.physics = new JoltPhysicsWrapper(scnMgr, cameraNode); - ECS::modified(); ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.import (); @@ -267,7 +263,6 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, #if 0 ecs.set({ 0 }); #endif - PhysicsModule::configurePhysics(); ecs.add(); ecs.add(); ecs.add(); diff --git a/src/gamedata/PhysicsModule.cpp b/src/gamedata/PhysicsModule.cpp index 50aa030..5cbaa15 100644 --- a/src/gamedata/PhysicsModule.cpp +++ b/src/gamedata/PhysicsModule.cpp @@ -625,16 +625,15 @@ void PhysicsModule::setDebugDraw(bool enable) JoltPhysicsWrapper::getSingleton().setDebugDraw(enable); } -void PhysicsModule::configurePhysics() -{ -} bool WaterBody::isInWater(const JPH::BodyID &id) const { +#if 0 flecs::entity e = ECS::get().query_builder().build().find( [&](const JPH::BodyID &bodyid) { return bodyid == id; }); +#endif return inWater.find(id) != inWater.end(); } } diff --git a/src/gamedata/PhysicsModule.h b/src/gamedata/PhysicsModule.h index 8701ad9..7100b0c 100644 --- a/src/gamedata/PhysicsModule.h +++ b/src/gamedata/PhysicsModule.h @@ -1,6 +1,7 @@ #ifndef _PHYSICS_MODULE_H_ #define _PHYSICS_MODULE_H_ #include +#include class JoltPhysicsWrapper; namespace JPH { @@ -61,7 +62,6 @@ struct PhysicsModule { const Ogre::Vector3 &endPos, Ogre::Vector3 &position, JPH::BodyID &id); static void setDebugDraw(bool enable); - static void configurePhysics(); }; } #endif