Compare commits
32 Commits
aca04ff621
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e0db570581 | |||
| 6eed5063e6 | |||
| 5b014dcb65 | |||
| cd82fb0eed | |||
| 3f0484e87c | |||
| 9c4bea5983 | |||
| 3645557520 | |||
| 19a1275a8a | |||
| 25280a9cbe | |||
| 7e06da700a | |||
| 9e5d08bfc6 | |||
| a62d781aa0 | |||
| fea7c71788 | |||
| e967844558 | |||
| 62e14cf075 | |||
| 4249a0238b | |||
| cfd9ed8708 | |||
| 1977a12d8b | |||
| 190318e5c4 | |||
| 1aa002d8ba | |||
| 1bc0f298fc | |||
| e6463fc264 | |||
| 7621607152 | |||
| 82ac145e87 | |||
| ff780c34b3 | |||
| 5c03f0cd2c | |||
| 4d0fb8f60f | |||
| 3f59a384e4 | |||
| d42cf2854a | |||
| 92ec3e9497 | |||
| 25816c5658 | |||
| 1c56387c35 |
1
.gitattributes
vendored
@@ -4,3 +4,4 @@
|
||||
*.vroid filter=lfs diff=lfs merge=lfs -text
|
||||
*.fbx filter=lfs diff=lfs merge=lfs -text
|
||||
*.wav filter=lfs diff=lfs merge=lfs -text
|
||||
*.ttf filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
64
.vscode/settings.json
vendored
@@ -9,6 +9,68 @@
|
||||
"istream": "cpp",
|
||||
"variant": "cpp",
|
||||
"tuple": "cpp",
|
||||
"iostream": "cpp"
|
||||
"iostream": "cpp",
|
||||
"string_view": "cpp",
|
||||
"atomic": "cpp",
|
||||
"memory_resource": "cpp",
|
||||
"stop_token": "cpp",
|
||||
"random": "cpp",
|
||||
"future": "cpp",
|
||||
"array": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"string": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"unordered_set": "cpp",
|
||||
"vector": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"span": "cpp",
|
||||
"chrono": "cpp",
|
||||
"format": "cpp",
|
||||
"cctype": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"bit": "cpp",
|
||||
"bitset": "cpp",
|
||||
"charconv": "cpp",
|
||||
"compare": "cpp",
|
||||
"concepts": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"map": "cpp",
|
||||
"set": "cpp",
|
||||
"exception": "cpp",
|
||||
"algorithm": "cpp",
|
||||
"functional": "cpp",
|
||||
"iterator": "cpp",
|
||||
"memory": "cpp",
|
||||
"optional": "cpp",
|
||||
"ratio": "cpp",
|
||||
"system_error": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"utility": "cpp",
|
||||
"fstream": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"limits": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numbers": "cpp",
|
||||
"numeric": "cpp",
|
||||
"ostream": "cpp",
|
||||
"semaphore": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"thread": "cpp",
|
||||
"typeinfo": "cpp"
|
||||
}
|
||||
}
|
||||
843
Bootstrap.cpp
160
CMakeLists.txt
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.13.0)
|
||||
project(world2)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(BLENDER "${CMAKE_SOURCE_DIR}/../../blender-bin/bin/blender" CACHE STRING "Blender path")
|
||||
set(CREATE_DIRECTORIES
|
||||
${CMAKE_BINARY_DIR}/assets/blender/shapes/male
|
||||
@@ -23,16 +23,14 @@ set(CREATE_DIRECTORIES
|
||||
# # INTERFACE_LINK_DIRECTORIES "${ASSIMP_LIBRARY_DIRS}"
|
||||
# INTERFACE_LINK_DIRECTORIES "${CMAKE_PREFIX_PATH}/lib"
|
||||
#)
|
||||
file(GLOB TERRAIN_SRC ${CMAKE_SOURCE_DIR}/src/terrain/*.cpp)
|
||||
file(GLOB WATER_SRC ${CMAKE_SOURCE_DIR}/water/*.cpp)
|
||||
|
||||
# The COMPONENTS part checks that OGRE was built the way we need it
|
||||
# The CONFIG flag makes sure we get OGRE instead of OGRE-next
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG)
|
||||
find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG)
|
||||
find_package(ZLIB)
|
||||
find_package(SDL2)
|
||||
find_package(assimp REQUIRED CONFIG)
|
||||
find_package(Bullet)
|
||||
find_package(OgreProcedural REQUIRED CONFIG)
|
||||
find_package(pugixml REQUIRED CONFIG)
|
||||
find_package(flecs REQUIRED CONFIG)
|
||||
@@ -67,39 +65,25 @@ set_target_properties(fix::pugixml PROPERTIES
|
||||
|
||||
|
||||
add_subdirectory(src/lua)
|
||||
add_subdirectory(src/characters)
|
||||
add_subdirectory(src/gamedata)
|
||||
add_subdirectory(src/miniaudio)
|
||||
add_subdirectory(src/sound)
|
||||
add_subdirectory(src/sceneloader)
|
||||
add_subdirectory(audio/gui)
|
||||
add_subdirectory(lua-scripts)
|
||||
add_subdirectory(morph)
|
||||
add_subdirectory(src/world)
|
||||
add_subdirectory(src/tests)
|
||||
add_subdirectory(src/physics)
|
||||
add_subdirectory(src/editor)
|
||||
|
||||
# add the source files as usual
|
||||
add_executable(0_Bootstrap Bootstrap.cpp)
|
||||
|
||||
# this also sets the includes and pulls third party dependencies
|
||||
target_link_libraries(0_Bootstrap OgreBites OgreBullet OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES}
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
target_link_options(0_Bootstrap PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(0_Bootstrap stage_files import_vrm)
|
||||
|
||||
add_executable(Editor Editor.cpp ${TERRAIN_SRC} ${WATER_SRC})
|
||||
target_link_libraries(Editor OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
target_link_options(Editor PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(Editor stage_files import_buildings import_water_stuff import_vehicles import_vrm)
|
||||
add_executable(Game Game.cpp ${TERRAIN_SRC} ${WATER_SRC})
|
||||
add_executable(Game Game.cpp ${WATER_SRC})
|
||||
target_include_directories(Game PRIVATE src/gamedata)
|
||||
target_link_libraries(Game OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator
|
||||
OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY}
|
||||
${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
target_link_libraries(Game OgreBites OgrePaging OgreTerrain OgreMeshLodGenerator
|
||||
OgreProcedural::OgreProcedural
|
||||
GameData
|
||||
sound
|
||||
sceneloader physics
|
||||
flecs::flecs_static
|
||||
-Wl,--as-needed
|
||||
)
|
||||
@@ -108,16 +92,28 @@ target_link_options(Game PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(Game stage_files import_buildings import_water_stuff import_vehicles import_vrm audio_data_gui)
|
||||
|
||||
add_executable(Procedural Procedural.cpp ${TERRAIN_SRC})
|
||||
target_link_libraries(Procedural OgreBites OgreBullet OgrePaging OgreTerrain
|
||||
OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY}
|
||||
${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
add_executable(Procedural Procedural.cpp)
|
||||
target_link_libraries(Procedural OgreBites OgrePaging OgreTerrain
|
||||
OgreProcedural::OgreProcedural
|
||||
-Wl,--as-needed
|
||||
)
|
||||
if(OGRE_STATIC)
|
||||
target_link_options(Procedural PRIVATE -static-libstdc++ -static-libgcc)
|
||||
endif()
|
||||
add_dependencies(Procedural stage_files import_buildings)
|
||||
file(GLOB FONTS_SRC ${CMAKE_SOURCE_DIR}/assets/fonts/*.ttf)
|
||||
set(FONT_OUTPUT_FILES)
|
||||
foreach(FONT_FILE ${FONTS_SRC})
|
||||
get_filename_component(FILE_NAME ${FONT_FILE} NAME_WE)
|
||||
set(FONT_OUTPUT_FILE ${CMAKE_BINARY_DIR}/resources/fonts/${FILE_NAME}.ttf)
|
||||
add_custom_command(
|
||||
OUTPUT ${FONT_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/resources/fonts
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${FONT_FILE} ${FONT_OUTPUT_FILE}
|
||||
DEPENDS ${FONT_FILE})
|
||||
list(APPEND FONT_OUTPUT_FILES ${FONT_OUTPUT_FILE})
|
||||
endforeach()
|
||||
add_custom_target(fonts ALL DEPENDS ${FONT_OUTPUT_FILES})
|
||||
file(GLOB BUILDINGS_SRC ${CMAKE_SOURCE_DIR}/assets/blender/buildings/*.blend)
|
||||
set(BUILDING_OUTPUT_FILES)
|
||||
foreach(BUILDING_FILE ${BUILDINGS_SRC})
|
||||
@@ -154,6 +150,25 @@ foreach(VEHICLE_FILE ${VEHICLES_SRC})
|
||||
endforeach()
|
||||
add_custom_target(import_vehicles ALL DEPENDS ${VEHICLE_OUTPUT_FILES})
|
||||
|
||||
set(CHARACTER_SHAPES_SRC edited-normal-male-base.blend edited-shape-test-male.blend)
|
||||
set(CHARACTER_SHAPES_OUTPUT_FILES)
|
||||
foreach(CHARACTER_SHAPE_FILE ${CHARACTER_SHAPES_SRC})
|
||||
get_filename_component(FILE_NAME ${CHARACTER_SHAPE_FILE} NAME_WE)
|
||||
set(CHARACTER_SHAPE_OUTPUT_FILE ${CMAKE_BINARY_DIR}/characters/shapes/male/${FILE_NAME}/${FILE_NAME}.scene)
|
||||
add_custom_command(
|
||||
OUTPUT ${CHARACTER_SHAPE_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/assets/blender
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/characters/shapes/male/${FILE_NAME}
|
||||
COMMAND ${BLENDER} ${CMAKE_SOURCE_DIR}/assets/blender/${CHARACTER_SHAPE_FILE}
|
||||
-b -Y -P
|
||||
${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_characters_ogre.py
|
||||
-- ${CHARACTER_SHAPE_OUTPUT_FILE}
|
||||
COMMAND touch ${CHARACTER_SHAPE_OUTPUT_FILE}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/${CHARACTER_SHAPE_FILE})
|
||||
list(APPEND CHARACTER_SHAPES_OUTPUT_FILES ${CHARACTER_SHAPE_OUTPUT_FILE})
|
||||
endforeach()
|
||||
add_custom_target(import_character_shapes ALL DEPENDS ${CHARACTER_SHAPES_OUTPUT_FILES})
|
||||
|
||||
set(WATER_STUFF)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/water/sea.glb
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/water
|
||||
@@ -166,8 +181,8 @@ add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/water/sea.glb
|
||||
list(APPEND WATER_STUFF ${CMAKE_BINARY_DIR}/water/sea.glb)
|
||||
add_custom_target(import_water_stuff ALL DEPENDS ${WATER_STUFF})
|
||||
|
||||
add_executable(TerrainTest terrain.cpp ${TERRAIN_SRC})
|
||||
target_link_libraries(TerrainTest OgreBites OgreBullet OgrePaging OgreTerrain lua ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}
|
||||
add_executable(TerrainTest terrain.cpp)
|
||||
target_link_libraries(TerrainTest OgreBites OgrePaging OgreTerrain lua
|
||||
-Wl,--as-needed
|
||||
)
|
||||
target_include_directories(TerrainTest PRIVATE . src/terrain src/lua src/lua/lua-5.4.8/src)
|
||||
@@ -175,23 +190,16 @@ if(OGRE_STATIC)
|
||||
target_link_libraries(TerrainTest fix::assimp pugixml)
|
||||
target_link_options(TerrainTest PRIVATE -static-libstdc++ -static-libgcc)
|
||||
target_link_libraries(Procedural fix::assimp pugixml)
|
||||
target_link_libraries(0_Bootstrap fix::assimp pugixml)
|
||||
target_link_libraries(Editor fix::assimp pugixml)
|
||||
endif()
|
||||
|
||||
file(GLOB LUA_SCRIPTS_SRC ${CMAKE_SOURCE_DIR}/lua-scripts/*.lua)
|
||||
set(LUA_SCRIPTS_OUTPUT)
|
||||
foreach(LUA_SCRIPT_FILE ${LUA_SCRIPTS_SRC})
|
||||
get_filename_component(FILE_NAME ${LUA_SCRIPT_FILE} NAME_WE)
|
||||
set(LUA_SCRIPT_OUTPUT_FILE ${CMAKE_BINARY_DIR}/lua-scripts/${FILE_NAME}.lua)
|
||||
add_custom_command(OUTPUT ${LUA_SCRIPT_OUTPUT_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${LUA_SCRIPT_FILE} ${LUA_SCRIPT_OUTPUT_FILE}
|
||||
DEPENDS ${LUA_SCRIPT_FILE})
|
||||
list(APPEND LUA_SCRIPTS_OUTPUT ${LUA_SCRIPT_OUTPUT_FILE})
|
||||
endforeach()
|
||||
add_custom_target(stage_lua_scripts ALL DEPENDS ${LUA_SCRIPTS_OUTPUT})
|
||||
add_dependencies(TerrainTest stage_lua_scripts stage_files)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/stories
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_BINARY_DIR}/lua-scripts/stories ${CMAKE_BINARY_DIR}/stories
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/lua-scripts/stories
|
||||
)
|
||||
add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR}/stories)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
|
||||
@@ -243,6 +251,8 @@ set(WATER_SRC
|
||||
water.program
|
||||
water.frag
|
||||
water.vert
|
||||
depth.frag
|
||||
depth.vert
|
||||
water.compositor
|
||||
waves2.png
|
||||
)
|
||||
@@ -271,9 +281,8 @@ endforeach()
|
||||
|
||||
file(GLOB VRM_FILES RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/vroid/*.vrm)
|
||||
file(GLOB MIXAMO_FILES RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/blender/mixamo/**/*.fbx)
|
||||
file(GLOB EDITED_BLENDS RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/blender/edited-*.blend)
|
||||
set(VRM_SOURCE)
|
||||
foreach(VRM_FILE ${VRM_FILES} ${MIXAMO_FILES} ${EDITED_BLENDS})
|
||||
foreach(VRM_FILE ${VRM_FILES} ${MIXAMO_FILES})
|
||||
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/${VRM_FILE}")
|
||||
set(INPUT_FILE "${CMAKE_SOURCE_DIR}/${VRM_FILE}")
|
||||
add_custom_command(OUTPUT "${OUTPUT_FILE}"
|
||||
@@ -296,13 +305,55 @@ add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS}
|
||||
${VRM_SOURCE}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
set(CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/male/normal-male.glb)
|
||||
set(EDITED_BLENDS_LIST
|
||||
male
|
||||
female
|
||||
)
|
||||
set(EDITED_BLEND_TARGETS)
|
||||
set(CHARACTER_GLBS)
|
||||
foreach(EDITED_BLEND ${EDITED_BLENDS_LIST})
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-E copy ${CMAKE_SOURCE_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
COMMAND ${BLENDER} -b -Y
|
||||
${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend
|
||||
-P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/copy_animations.py --
|
||||
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend ${EDITED_BLEND}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend)
|
||||
list(APPEND CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/${EDITED_BLEND}/normal-${EDITED_BLEND}.glb)
|
||||
endforeach()
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/assets/blender/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/assets/blender/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
add_custom_target(morph ALL DEPENDS MorphTargetsResearch ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
|
||||
set(COPY_BLENDS edited-shape-test-male.blend edited-normal-male-base.blend)
|
||||
foreach (COPY_BLEND_FILE ${COPY_BLENDS})
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
${CMAKE_SOURCE_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
)
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(edited-blends ALL DEPENDS ${EDITED_BLEND_TARGETS})
|
||||
|
||||
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_BLENDS}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
#add_custom_command(
|
||||
# OUTPUT ${CMAKE_SOURCE_DIR}/characters/female/vroid-normal-female.scene
|
||||
@@ -325,11 +376,14 @@ add_custom_command(
|
||||
|
||||
add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MATERIALS_OUTPUT}
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png)
|
||||
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
|
||||
edited-blends)
|
||||
|
||||
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})
|
||||
|
||||
add_custom_target(import_vrm DEPENDS ${CHARACTER_GLBS})
|
||||
target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
|
||||
|
||||
install(TARGETS Game DESTINATION bin)
|
||||
install(TARGETS Editor DESTINATION bin)
|
||||
|
||||
|
||||
26
Editor.cpp
@@ -9,8 +9,8 @@
|
||||
#include <OgreTrays.h>
|
||||
#include <OgreTimer.h>
|
||||
#include <OgreMeshLodGenerator.h>
|
||||
#include <OgreTerrain.h>
|
||||
|
||||
#include "src/terrain/terrain.h"
|
||||
#include "water/water.h"
|
||||
class App;
|
||||
class SkyRenderer : public Ogre::SceneManager::Listener {
|
||||
@@ -244,16 +244,12 @@ public:
|
||||
void initGui();
|
||||
};
|
||||
class App : public OgreBites::ApplicationContext {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal;
|
||||
Ogre::Camera *mCamera;
|
||||
Ogre::Real mPivotPitch;
|
||||
Ogre::SceneManager *mScnMgr;
|
||||
OgreBites::InputListenerChain mInput;
|
||||
Ogre::Viewport *mViewport;
|
||||
EditUI m_edit_ui;
|
||||
TerrainSetup m_terrain;
|
||||
Ogre::Light *mSun;
|
||||
SkyBoxRenderer *sky;
|
||||
Water m_water;
|
||||
@@ -377,9 +373,6 @@ public:
|
||||
: OgreBites::ApplicationContext("ChoroEditor")
|
||||
, mKbd(this)
|
||||
, m_edit_ui(this)
|
||||
, mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, m_terrain(mDynWorld->getBtWorld())
|
||||
{
|
||||
}
|
||||
virtual ~App()
|
||||
@@ -395,8 +388,6 @@ public:
|
||||
mScnMgr->addRenderQueueListener(pOverlaySystem);
|
||||
// mTrayMgr = new OgreBites::TrayManager("AppTrays",
|
||||
// getRenderWindow());
|
||||
mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(
|
||||
mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld()));
|
||||
}
|
||||
void locateResources() override
|
||||
{
|
||||
@@ -457,8 +448,10 @@ public:
|
||||
std::cout << "Init camera" << "\n";
|
||||
initCamera();
|
||||
std::cout << "Set up water" << "\n";
|
||||
#if 0
|
||||
m_water.createWater(getRenderWindow(), mCamera,
|
||||
mDynWorld.get());
|
||||
#endif
|
||||
std::cout << "Set up cursor" << "\n";
|
||||
Ogre::ResourceGroupManager::getSingleton()
|
||||
.initialiseAllResourceGroups();
|
||||
@@ -467,7 +460,6 @@ public:
|
||||
std::cout << "Create content" << "\n";
|
||||
createContent();
|
||||
std::cout << "Setup terrain" << "\n";
|
||||
setupTerrain();
|
||||
m_water.init();
|
||||
}
|
||||
void setupCursor()
|
||||
@@ -514,6 +506,7 @@ public:
|
||||
}
|
||||
void updateMotion(float delta)
|
||||
{
|
||||
#if 0
|
||||
if (delta == 0.0f)
|
||||
return;
|
||||
Ogre::Vector3 move(mCameraNode->getOrientation().zAxis() *
|
||||
@@ -536,6 +529,7 @@ public:
|
||||
// mKbd.motion = Ogre::Vector3(0, 0, 0);
|
||||
// if (move.squaredLength() > 0)
|
||||
// std::cout << move << "\n";
|
||||
#endif
|
||||
}
|
||||
void updateCamera(Ogre::Real delta)
|
||||
{
|
||||
@@ -582,10 +576,12 @@ public:
|
||||
mCameraGoal->translate(0, 0, distChange,
|
||||
Ogre::Node::TS_LOCAL);
|
||||
Ogre::Vector3 mh = mCameraGoal->_getDerivedPosition();
|
||||
#if 0
|
||||
float h = m_terrain.get_height(mh);
|
||||
if (h + 10 > mh.y)
|
||||
mCameraGoal->translate(0, 10.0f * deltaZoom, distChange,
|
||||
Ogre::Node::TS_LOCAL);
|
||||
#endif
|
||||
}
|
||||
Ogre::SceneNode *mSunGoal;
|
||||
Ogre::SceneNode *mSunNode;
|
||||
@@ -659,9 +655,6 @@ public:
|
||||
m_edit_ui.init_glb_list();
|
||||
m_edit_ui.initGui();
|
||||
createSun();
|
||||
mInput = OgreBites::InputListenerChain(
|
||||
{ getImGuiInputListener(), &mKbd });
|
||||
addInputListener(&mInput);
|
||||
getRoot()->addFrameListener(&mKbd);
|
||||
// mTrayMgr->showCursor();
|
||||
|
||||
@@ -717,11 +710,6 @@ public:
|
||||
{
|
||||
return mCamera;
|
||||
}
|
||||
void setupTerrain()
|
||||
{
|
||||
m_terrain.setupTerrain(mCamera, mSun, mDynWorld.get(),
|
||||
mDbgDraw.get());
|
||||
}
|
||||
};
|
||||
|
||||
void EditUI::buildings_editor()
|
||||
|
||||
209
Procedural.cpp
@@ -7,9 +7,6 @@
|
||||
|
||||
#include "Ogre.h"
|
||||
#include "OgreApplicationContext.h"
|
||||
#include "Bullet/OgreBullet.h"
|
||||
#include "BulletCollision/CollisionDispatch/btGhostObject.h"
|
||||
#include "LinearMath/btTransform.h"
|
||||
#include "OgrePageManager.h"
|
||||
#include "Procedural.h"
|
||||
|
||||
@@ -22,11 +19,8 @@ using Real = Ogre::Real;
|
||||
using Math = Ogre::Math;
|
||||
|
||||
class WorldData {
|
||||
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
|
||||
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
|
||||
std::unique_ptr<Ogre::Root> mRoot;
|
||||
std::unique_ptr<Ogre::SceneManager> mScnMgr;
|
||||
std::unique_ptr<btDynamicsWorld> mbtWorld;
|
||||
std::unique_ptr<Ogre::PageManager> mPageManager;
|
||||
Ogre::PagedWorld *mPagedWorld;
|
||||
|
||||
@@ -62,13 +56,8 @@ private:
|
||||
DummyPageProvider mDummyPageProvider;
|
||||
|
||||
WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr)
|
||||
: mDynWorld(new Ogre::Bullet::DynamicsWorld(
|
||||
Ogre::Vector3(0, -9.8, 0)))
|
||||
, mDbgDraw(new Ogre::Bullet::DebugDrawer(
|
||||
scnMgr->getRootSceneNode(), mDynWorld->getBtWorld()))
|
||||
, mRoot(root)
|
||||
: mRoot(root)
|
||||
, mScnMgr(scnMgr)
|
||||
, mbtWorld(mDynWorld->getBtWorld())
|
||||
, mPageManager(nullptr)
|
||||
, mPagedWorld(nullptr)
|
||||
{
|
||||
@@ -100,94 +89,8 @@ public:
|
||||
void createTrimesh(Ogre::Entity *entity)
|
||||
{
|
||||
}
|
||||
btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent,
|
||||
btCollisionShape *shape,
|
||||
int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
{
|
||||
btDynamicsWorld *world = mDynWorld->getBtWorld();
|
||||
Ogre::SceneNode *node = ent->getParentSceneNode();
|
||||
btPairCachingGhostObject *ghost =
|
||||
new btPairCachingGhostObject();
|
||||
ghost->setCollisionShape(shape);
|
||||
ghost->setCollisionFlags(
|
||||
ghost->getCollisionFlags() |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE |
|
||||
btCollisionObject::CF_CHARACTER_OBJECT);
|
||||
getWorld()->attachCollisionObject(ghost, ent, group, mask);
|
||||
#if 0
|
||||
getBtWorld()
|
||||
->getBroadphase()->getOverlappingPairCache()
|
||||
->setInternalGhostPairCallback(new btGhostPairCallback());
|
||||
ghost->setUserPointer(new EntityCollisionListener{ent, nullptr});
|
||||
#endif
|
||||
return ghost;
|
||||
}
|
||||
btRigidBody *addRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct, int group = 1,
|
||||
int mask = 0xFFFF)
|
||||
{
|
||||
btDynamicsWorld *world = mDynWorld->getBtWorld();
|
||||
Ogre::SceneNode *node = ent->getParentSceneNode();
|
||||
Ogre::Bullet::RigidBodyState *state =
|
||||
new Ogre::Bullet::RigidBodyState(node);
|
||||
btCollisionShape *cs;
|
||||
btCollisionShape *shape;
|
||||
btVector3 inertia(0, 0, 0);
|
||||
switch (ct) {
|
||||
case Ogre::Bullet::CT_TRIMESH: {
|
||||
cs = Ogre::Bullet::createTrimeshCollider(ent);
|
||||
if (mass != 0)
|
||||
cs->calculateLocalInertia(mass, inertia);
|
||||
} break;
|
||||
case Ogre::Bullet::CT_CAPSULE: {
|
||||
cs = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
shape = new btCapsuleShape(radius,
|
||||
2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(cs)->addChildShape(
|
||||
transform, shape);
|
||||
btScalar masses[1] = { mass };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(cs)
|
||||
->calculatePrincipalAxisTransform(
|
||||
masses, principal, inertia);
|
||||
} break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
btRigidBody *body = new btRigidBody(mass, state, cs, inertia);
|
||||
getWorld()->attachRigidBody(body, ent, nullptr, group, mask);
|
||||
#if 0
|
||||
body->setUserPointer(new EntityCollisionListener{ent, nullptr});
|
||||
// btRigidBody *body = mDynWorld->addRigidBody(0, ent, Ogre::Bullet::CT_TRIMESH);
|
||||
#endif
|
||||
return body;
|
||||
}
|
||||
btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent,
|
||||
Ogre::Bullet::ColliderType ct,
|
||||
int group = 1, int mask = 0xFFFF)
|
||||
{
|
||||
return mDynWorld->addKinematicRigidBody(ent, ct, group, mask);
|
||||
}
|
||||
btDynamicsWorld *getBtWorld()
|
||||
{
|
||||
return mDynWorld->getBtWorld();
|
||||
}
|
||||
Ogre::Bullet::DynamicsWorld *getWorld()
|
||||
{
|
||||
return mDynWorld.get();
|
||||
}
|
||||
void update(float delta)
|
||||
{
|
||||
WorldData::get_singleton()->getBtWorld()->stepSimulation(delta,
|
||||
10);
|
||||
mDbgDraw->update();
|
||||
}
|
||||
void initPagedWorld(Ogre::Camera *camera)
|
||||
{
|
||||
@@ -201,8 +104,6 @@ public:
|
||||
WorldData *WorldData::singleton = nullptr;
|
||||
|
||||
class MainWorld : public Ogre::FrameListener {
|
||||
btRigidBody *mFloorBody;
|
||||
|
||||
public:
|
||||
void setup()
|
||||
{
|
||||
@@ -220,13 +121,6 @@ public:
|
||||
WorldData::get_singleton()->getSceneManager();
|
||||
Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor");
|
||||
scnMgr->getRootSceneNode()->attachObject(floor);
|
||||
mFloorBody = WorldData::get_singleton()->addRigidBody(
|
||||
0, floor, Ogre::Bullet::CT_TRIMESH);
|
||||
}
|
||||
btRigidBody *addCharacter(Ogre::Entity *ent, float mass)
|
||||
{
|
||||
return WorldData::get_singleton()->addKinematicRigidBody(
|
||||
mass, ent, Ogre::Bullet::CT_COMPOUND);
|
||||
}
|
||||
bool frameStarted(const Ogre::FrameEvent &evt) override;
|
||||
};
|
||||
@@ -266,8 +160,6 @@ class CharacterController : public OgreBites::InputListener,
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Quaternion rootRotation;
|
||||
// btRigidBody *mRigidBody;
|
||||
btCompoundShape *mCollisionShape;
|
||||
btPairCachingGhostObject *mGhostObject;
|
||||
|
||||
public:
|
||||
CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam,
|
||||
@@ -316,35 +208,6 @@ private:
|
||||
recoverResult *recover_result,
|
||||
const std::set<btCollisionObject *> &exclude);
|
||||
#endif
|
||||
inline btQuaternion convert(const Ogre::Quaternion &q)
|
||||
{
|
||||
return btQuaternion(q.x, q.y, q.z, q.w);
|
||||
}
|
||||
inline btVector3 convert(const Ogre::Vector3 &v)
|
||||
{
|
||||
return btVector3(v.x, v.y, v.z);
|
||||
}
|
||||
inline btTransform convert(const Ogre::Quaternion &q,
|
||||
const Ogre::Vector3 &v)
|
||||
{
|
||||
btQuaternion mq = convert(q);
|
||||
btVector3 mv = convert(v);
|
||||
return btTransform(mq, mv);
|
||||
}
|
||||
inline Ogre::Quaternion convert(const btQuaternion &q)
|
||||
{
|
||||
return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z());
|
||||
}
|
||||
inline Ogre::Vector3 convert(const btVector3 &v)
|
||||
{
|
||||
return Ogre::Vector3(v.x(), v.y(), v.z());
|
||||
}
|
||||
inline void convert(const btTransform &from, Ogre::Quaternion &q,
|
||||
Ogre::Vector3 &v)
|
||||
{
|
||||
q = convert(from.getRotation());
|
||||
v = convert(from.getOrigin());
|
||||
}
|
||||
};
|
||||
CharacterController::CharacterController(Ogre::SceneNode *camNode,
|
||||
Ogre::Camera *cam,
|
||||
@@ -358,8 +221,6 @@ CharacterController::CharacterController(Ogre::SceneNode *camNode,
|
||||
, mAnimID(ANIM_NONE)
|
||||
, mRunning(false)
|
||||
, world(world)
|
||||
, mCollisionShape(nullptr)
|
||||
, mGhostObject(nullptr)
|
||||
{
|
||||
setupBody();
|
||||
setupCamera();
|
||||
@@ -374,72 +235,6 @@ void CharacterController::setupBody()
|
||||
mBodyNode = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
mBodyNode->attachObject(mBodyEnt);
|
||||
mSkeleton = mBodyEnt->getSkeleton();
|
||||
// mRigidBody = world->addCharacter(mBodyEnt, 0);
|
||||
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
|
||||
mGhostObject = new btPairCachingGhostObject();
|
||||
mCollisionShape = new btCompoundShape;
|
||||
mGhostObject->setCollisionShape(mCollisionShape);
|
||||
|
||||
{
|
||||
btVector3 inertia(0, 0, 0);
|
||||
// mCollisionShape = new btCompoundShape();
|
||||
btScalar height = 1.0f;
|
||||
btScalar radius = 0.3f;
|
||||
btCapsuleShape *shape =
|
||||
new btCapsuleShape(radius, 2 * height - 2 * radius);
|
||||
btTransform transform;
|
||||
transform.setIdentity();
|
||||
transform.setOrigin(btVector3(0, 1, 0));
|
||||
static_cast<btCompoundShape *>(mCollisionShape)
|
||||
->addChildShape(transform, shape);
|
||||
btScalar masses[1] = { 0 };
|
||||
btTransform principal;
|
||||
static_cast<btCompoundShape *>(mCollisionShape)
|
||||
->calculatePrincipalAxisTransform(masses, principal,
|
||||
inertia);
|
||||
}
|
||||
mGhostObject->setCollisionFlags(
|
||||
btCollisionObject::CF_KINEMATIC_OBJECT |
|
||||
btCollisionObject::CF_NO_CONTACT_RESPONSE);
|
||||
mGhostObject->setActivationState(DISABLE_DEACTIVATION);
|
||||
Ogre::Bullet::KinematicMotionSimple *controller =
|
||||
new Ogre::Bullet::KinematicMotionSimple(mGhostObject,
|
||||
mBodyNode);
|
||||
WorldData::get_singleton()->getWorld()->attachCollisionObject(
|
||||
mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter,
|
||||
btBroadphaseProxy::AllFilter);
|
||||
WorldData::get_singleton()->getBtWorld()->addAction(controller);
|
||||
|
||||
assert(mCollisionShape);
|
||||
#if 0
|
||||
if (mRigidBody->getMass() == 0) {
|
||||
#if 0
|
||||
mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags()
|
||||
| btCollisionObject::CF_KINEMATIC_OBJECT
|
||||
| btCollisionObject::CF_NO_CONTACT_RESPONSE
|
||||
);
|
||||
#endif
|
||||
#if 0
|
||||
mGhostObject->setWorldTransform(mRigidBody->getWorldTransform());
|
||||
WorldData::get_singleton()->getBtWorld()
|
||||
->getBroadphase()->getOverlappingPairCache()
|
||||
->setInternalGhostPairCallback(new btGhostPairCallback());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
mRigidBody->setActivationState(DISABLE_DEACTIVATION);
|
||||
#endif
|
||||
#if 0
|
||||
{
|
||||
Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb");
|
||||
Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode();
|
||||
e2node->attachObject(e2);
|
||||
mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape);
|
||||
mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f);
|
||||
WorldData::get_singleton()->getBtWorld()->addAction(mController);
|
||||
}
|
||||
#endif
|
||||
assert(mSkeleton->hasBone("Root"));
|
||||
mRootBone = mSkeleton->getBone("Root");
|
||||
assert(mRootBone);
|
||||
@@ -740,8 +535,6 @@ void CharacterController::updateRootMotion(Real delta)
|
||||
Ogre::Vector3 velocity = rot * boneMotion / delta;
|
||||
velocity += gravity * delta;
|
||||
Ogre::Vector3 rotMotion = velocity * delta;
|
||||
btTransform from(convert(mBodyNode->getOrientation()),
|
||||
convert(mBodyNode->getPosition()));
|
||||
mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion);
|
||||
// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true,
|
||||
// nullptr, false, std::set<btCollisionObject *>());
|
||||
|
||||
BIN
assets/blender/altar.blend
(Stored with Git LFS)
BIN
assets/blender/buildings/altar.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/edited-normal-female.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/edited-normal-male-base.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/edited-normal-male-complete.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/edited-normal-male.blend
(Stored with Git LFS)
BIN
assets/blender/edited-shape-test-male.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/hanging-climb.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/hanging-idle.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/sitting.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/swimming.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/female/treading_water.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/hanging-climb.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/hanging-idle.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/sitting.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/swimming.fbx
(Stored with Git LFS)
Normal file
BIN
assets/blender/mixamo/male/treading_water.fbx
(Stored with Git LFS)
Normal file
38
assets/blender/scripts/blender2ogre/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve `blender2ogre`
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!--
|
||||
If you have a question rather than reporting a bug please go to
|
||||
- https://gitter.im/OGRECave/ogre or
|
||||
- https://forums.ogre3d.org/
|
||||
where you will get much faster responses.
|
||||
|
||||
This is a template helping you to create an issue which can be processed as quickly as possible.
|
||||
-->
|
||||
|
||||
#### System Information
|
||||
- Ogre Version: :grey_question:
|
||||
- Operating System / Platform: :grey_question:
|
||||
- Blender Version: :grey_question:
|
||||
|
||||
#### Detailed description
|
||||
<!-- your description -->
|
||||
|
||||
#### blender2ogre.log
|
||||
<!-- when attaching code or the log use triple backticks as below:
|
||||
```
|
||||
paste your log here
|
||||
```
|
||||
-->
|
||||
|
||||
#### OgreXMLConverter.log or OgreMeshTool.log
|
||||
<!-- when attaching code or the log use triple backticks as below:
|
||||
```
|
||||
paste your log here
|
||||
```
|
||||
-->
|
||||
25
assets/blender/scripts/blender2ogre/.github/workflows/ci-build.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: CI Build
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y blender ogre-1.12-tools
|
||||
- uses: actions/checkout@v2
|
||||
- name: Test
|
||||
run: |
|
||||
mkdir -p ~/.config/blender/3.0/scripts/addons/
|
||||
ln -s `pwd`/io_ogre ~/.config/blender/3.0/scripts/addons/
|
||||
blender examples/armature-test.blend -b --python test/run.py
|
||||
# verify that files were created
|
||||
test -f test.scene
|
||||
test -f Cube.mesh
|
||||
test -f Cube.skeleton
|
||||
test -f Material.material
|
||||
26
assets/blender/scripts/blender2ogre/.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
syntax: glob
|
||||
# This line is a comment, and will be skipped.
|
||||
# Empty lines are skipped too.
|
||||
|
||||
# Backup/Lock files left behind by the Emacs editor.
|
||||
*~
|
||||
\#*.*\#
|
||||
|
||||
# Lock files used by the Emacs editor.
|
||||
# Notice that the "#" character is quoted with a backslash.
|
||||
# This prevents it from being interpreted as starting a comment.
|
||||
.\#*
|
||||
|
||||
# Temporary files used by the vim editor.
|
||||
.*.swp
|
||||
|
||||
# A hidden file created by the Mac OS X Finder.
|
||||
.DS_Store
|
||||
|
||||
# python bytecode
|
||||
*.pyc
|
||||
|
||||
__pycache__
|
||||
_ogre_debug.txt
|
||||
test/blender/
|
||||
|
||||
68
assets/blender/scripts/blender2ogre/CHANGELOG.txt
Normal file
@@ -0,0 +1,68 @@
|
||||
CHANGELOG
|
||||
0.6.2
|
||||
* pulled fixed added to the monoltic python file
|
||||
* added exporting of custom vertex groups
|
||||
* console export documented
|
||||
0.6.1
|
||||
* code refactored, tested to work with 2.71
|
||||
0.6.0
|
||||
* patched to work with 2.66.
|
||||
0.5.9
|
||||
* apply patch from Thomas for Blender 2.6x support
|
||||
0.5.8
|
||||
* Clean all names that will be used as filenames on disk. Adjust all places
|
||||
that use these names for refs instead of ob.name/ob.data.name. Replaced chars
|
||||
are \, /, :, *, ?, ", <, >, | and spaces. Tested on work with ogre
|
||||
material, mesh and skeleton writing/refs inside the files and txml refs.
|
||||
Shows warning at final report if we had to resort to the renaming so user
|
||||
can possibly rename the object.
|
||||
* Added silent auto update checks if blender2ogre was installed using
|
||||
the .exe installer. This will keep people up to date when new versions are out.
|
||||
* Fix tracker issue 48: Needs to check if outputting to /tmp or
|
||||
~/.wine/drive_c/tmp on Linux. Thanks to vax456 for providing the patch,
|
||||
added him to contributors. Preview mesh's are now placed under /tmp
|
||||
on Linux systems if the OgreMeshy executable ends with .exe
|
||||
* Fix tracker issue 46: add operationtype to <submesh>
|
||||
* Implement a modal dialog that reports if material names have invalid
|
||||
characters and cant be saved on disk. This small popup will show until
|
||||
user presses left or right mouse (anywhere).
|
||||
* Fix tracker issue 44: XML Attributes not properly escaped in .scene file
|
||||
* Implemented reading OgreXmlConverter path from windows registry.
|
||||
The .exe installer will ship with certain tools so we can stop guessing
|
||||
and making the user install tools separately and setting up paths.
|
||||
* Fix bug that .mesh files were not generated while doing a .txml export.
|
||||
This was result of the late 2.63 mods that forgot to update object
|
||||
facecount before determining if mesh should be exported.
|
||||
* Fix bug that changed settings in the export dialog were forgotten when you
|
||||
re-exported without closing blender. Now settings should persist always
|
||||
from the last export. They are also stored to disk so the same settings
|
||||
are in use when if you restart Blender.
|
||||
* Fix bug that once you did a export, the next time the export location was
|
||||
forgotten. Now on sequential exports, the last export path is remembered in
|
||||
the export dialog.
|
||||
* Remove all local:// from asset refs and make them relative in .txml export.
|
||||
Having relative refs is the best for local preview and importing the txml
|
||||
to existing scenes.
|
||||
* Make .material generate what version of this plugins was used to generate
|
||||
the material file. Will be helpful in production to catch things.
|
||||
Added pretty printing line endings so the raw .material data is easier to read.
|
||||
* Improve console logging for the export stages. Run Blender from
|
||||
cmd prompt to see this information.
|
||||
* Clean/fix documentation in code for future development
|
||||
* Add todo to code for future development
|
||||
* Restructure/move code for easier readability
|
||||
* Remove extra white spaces and convert tabs to space
|
||||
0.5.7
|
||||
* Update to Blender 2.6.3.
|
||||
* Fixed xz-y Skeleton rotation (again)
|
||||
* Added additional Keyframe at the end of each animation to prevent
|
||||
ogre from interpolating back to the start
|
||||
* Added option to ignore non-deformable bones
|
||||
* Added option to export nla-strips independently from each other
|
||||
|
||||
TODO
|
||||
* Remove this section and integrate below with code :)
|
||||
* Fix terrain collision offset bug
|
||||
* Add realtime transform (rotation is missing)
|
||||
* Fix camera rotated -90 ogre-dot-scene
|
||||
* Set description field for all pyRNA
|
||||
82
assets/blender/scripts/blender2ogre/CustomSplitNormals.md
Normal file
@@ -0,0 +1,82 @@
|
||||
|
||||
# Custom Split Normals
|
||||
Custom Split Normals is a way to tweak the mesh shading by pointing normals towards directions other than the default, auto-computed ones.
|
||||
It is mostly used in game development, where it helps counterbalance some issues generated by low-poly objects
|
||||
(the most common examples are low-poly trees, bushes, grass, etc. and the ‘rounded’ corners).
|
||||
|
||||
## Documentation
|
||||
- [Normals - Blender Manual](https://docs.blender.org/manual/en/latest/modeling/meshes/structure.html#modeling-meshes-normals-custom)
|
||||
|
||||
The first step towards working with Custom Normals is having Blender show them.
|
||||
|
||||
Select the object you are working with, enter `Edit Mode`, and in the upper right corner, there is an `Overlays` menu.
|
||||
In the `Overlays` menu we have the option of displaying "Vertex Normals", "Split Normals" and "Face Normals"
|
||||
|
||||

|
||||
|
||||
In this example, we are showing both "Vertex Normals" (colored blue) and "Split Normals" (colored pink)
|
||||
|
||||
## Custom Normals
|
||||
[Blender Normal Editing TOOLS (In 2 Minutes!!)](https://www.youtube.com/watch?v=hwhM437Xvks)
|
||||
|
||||
Custom Normals refer to the feature of being able to modify the vertex normals as one sees fit.
|
||||
This can be useful for example in anime characters that usually need specific shading profiles to look good with the toon shader.
|
||||
One way to accomplish that is by editing the normals of the Mesh.
|
||||
|
||||
There are many ways to edit the Normals, one option is to use modifiers and another is to edit the normals directly.
|
||||
|
||||

|
||||
|
||||
Please consult the video and documentation to know more about editing normals in this way
|
||||
|
||||
## Sharp Edges
|
||||
One of the uses for Custom Normals is to have hard edges on an otherwise smooth mesh.
|
||||
This allows for a simpler geometry since a smooth mesh gets exported to fewer vertices.
|
||||
(That is because to have flat shading in OGRE, there have to be as many vertices as normals)
|
||||
|
||||

|
||||
|
||||
In the Smooth vs Flat shading world, there is a third option in Blender: Auto Smooth, or Smooth with hard edges
|
||||
|
||||

|
||||
|
||||
Thanks to the Auto Smooth feature, it is possible to have hard edges but also smooth ones in the same mesh.
|
||||
(This example mesh was made by adding a `Boolean` modifier to the Cube and then performing a Union operation with the Cylinder)
|
||||
|
||||
To accomplish this kind of shading, select the Object and make sure you are in `Object Mode`.
|
||||
Then set the Object shading to "Shade Smooth" (Object -> Shade Smooth (Blender 2.8+))
|
||||
|
||||

|
||||
|
||||
Then enable "Auto Smooth": (▽ -> Normals -> Auto Smooth) and choose a proper angle.
|
||||
|
||||
If you are happy with the results of "Auto Smooth", then enter `Edit Mode` and click on `Add Custom Split Normal Data` (▽ -> Geometry Data -> Add Custom Split Normals Data).
|
||||
|
||||
With this operation, Blender automatically marks the Sharp Edges in the mesh and stores the "Custom Split Normal Data" for `blender2ogre`.
|
||||
|
||||
If you need to mark other Edges as sharp, because the "Auto Smooth" is insufficient then it is possible to mark more Edges as sharp.
|
||||
|
||||
Make sure you are in `Edit Mode` and `Edge Selection Mode`, select the Edges you want to mark as sharp, and then select the option `Edge->Mark Sharp`
|
||||
|
||||

|
||||
|
||||
|
||||
## Fillet Edges
|
||||
[A short explanation about custom vertex normals](https://polycount.com/discussion/154664/a-short-explanation-about-custom-vertex-normals-tutorial)
|
||||
[Blender: Understanding Custom Split Normals](https://www.youtube.com/watch?v=o-jr_q_pkF0)
|
||||
Another use for Custom Normals is to have smooth edges (also called Fillet Edges).
|
||||
|
||||

|
||||
|
||||
To get this kind of shading, select the object.
|
||||
|
||||
Add the `Bevel` Modifier, then add the `Weighted Normal` modifier.
|
||||
|
||||

|
||||
|
||||
The option `Clamp Overlap` should be disabled in some cases where the geometry of the mesh is more complex.
|
||||
|
||||
For the `Weighted Normal` modifier to work well, you have to enable "Auto Smooth": (▽ -> Normals -> Auto Smooth)
|
||||
|
||||
> NOTE: Until this is fixed, for the Fillet Edges to look correct in the exported Mesh you have to create a copy of the object, apply the modifiers then go into `Edit Mode` and perform a manual triangulation of the Mesh (Face -> Triangulate Faces or Ctrl-T)
|
||||
|
||||
140
assets/blender/scripts/blender2ogre/LICENSE.txt
Normal file
@@ -0,0 +1,140 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change.
|
||||
c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License.
|
||||
d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful.
|
||||
(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.)
|
||||
b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with.
|
||||
c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution.
|
||||
d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place.
|
||||
e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy.
|
||||
For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above.
|
||||
b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
|
||||
8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
72
assets/blender/scripts/blender2ogre/MaterialsJSON.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# OGRE Next JSON Materials
|
||||
Support implemented based on the Ogre Next documentation.
|
||||
https://ogrecave.github.io/ogre-next/api/latest/hlmspbsdatablockref.html
|
||||
|
||||
Only the metallic workflow is supported at this time.
|
||||
|
||||
## Metallic Workflow
|
||||
Metalness texture fetching expects a single image with the metal
|
||||
texture in the Blue channel and the roughness texture in the Green
|
||||
channel. The channels are expected to have been split via a 'Separate RGB' node
|
||||
before passing to the Principled BSDF. This is in line with the glTF standard
|
||||
setup.
|
||||
|
||||
## Specular Workflow
|
||||
Unsupported.
|
||||
|
||||
## Unsupported features
|
||||
|
||||
### emissive.use_emissive_lightmap
|
||||
This requires features only found in Blender 2.9+
|
||||
|
||||
### fresnel
|
||||
This is used in the Specular workflows supported by Ogre. Right now we
|
||||
only support the metallic workflow.
|
||||
|
||||
### blendblock
|
||||
Blendblocks are used for advanced effects and don't fit into the
|
||||
standard Blender workflow. One commmon use would be to have better
|
||||
alpha blending on complex textures. Limit of 32 blend blocks at
|
||||
runtime also means we shouldn't "just generate them anyway."
|
||||
doc: https://ogrecave.github.io/ogre-next/api/latest/hlmsblendblockref.html
|
||||
|
||||
### macroblock
|
||||
Macroblocks are used for advanced effects and don't fit into the
|
||||
standard Blender workflow. One common use would be to render a skybox
|
||||
behind everything else in a scene. Limit of 32 macroblocks at runtime
|
||||
also means we shouldn't "just generate them anyway."
|
||||
doc: https://ogrecave.github.io/ogre-next/api/latest/hlmsmacroblockref.html
|
||||
|
||||
### sampler
|
||||
Samplerblocks are used for advanced texture handling like filtering,
|
||||
addressing, LOD, etc. These settings have signifigant visual and
|
||||
performance effects. Limit of 32 samplerblocks at runtime also means
|
||||
we shouldn't "just generate them anyway."
|
||||
|
||||
### recieve_shadows
|
||||
No receive shadow setting in Blender 2.8+ but was available in 2.79.
|
||||
We leave this unset which defaults to true. Maybe add support in
|
||||
the 2.7 branch?
|
||||
See: https://docs.blender.org/manual/en/2.79/render/blender_render/materials/properties/shadows.html#shadow-receiving-object-material
|
||||
|
||||
### shadow_const_bias
|
||||
Leave shadow const bias undefined to default. It is usually used to
|
||||
fix specific self-shadowing issues and is an advanced feature.
|
||||
|
||||
### brdf
|
||||
Leave brdf undefined to default. This setting has huge visual and
|
||||
performance impacts and is for specific use cases.
|
||||
doc: https://ogrecave.github.io/ogre-next/api/latest/hlmspbsdatablockref.html#dbParamBRDF
|
||||
|
||||
### reflection
|
||||
Leave reflection undefined to default. In most cases for reflections
|
||||
users will want to use generated cubemaps in-engine.
|
||||
|
||||
### detail_diffuse[0-3]
|
||||
Layered diffuse maps for advanced effects.
|
||||
|
||||
### detail_normal[0-3]
|
||||
Layered normal maps for advanced effects.
|
||||
|
||||
### detail_weight
|
||||
Texture acting as a mask for the detail maps.
|
||||
91
assets/blender/scripts/blender2ogre/MeshTriangulation.md
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
# Mesh Triangulation
|
||||
|
||||
While Blender can create meshes featuring triangles, quads and n-gons, OGRE3D being a realtime rendering engine deals only with triangles.
|
||||
That means that blender2ogre needs to perform a process of mesh triangulation to export the meshes.
|
||||
Sometimes this triangulation process leads to some problems described here.
|
||||
|
||||
## Documentation
|
||||
- [How can I manually fix nonplanar geometry?](https://blender.stackexchange.com/questions/119874/how-can-i-manually-fix-nonplanar-geometry)
|
||||
- [How to flatten a face to avoid distortion (make an ngon planar)](https://blender.stackexchange.com/questions/7729/how-to-flatten-a-face-to-avoid-distortion-make-an-ngon-planar)
|
||||
- [How do I fix this issue with weird faces?](https://blender.stackexchange.com/questions/55466/how-do-i-fix-this-issue-with-weird-faces)
|
||||
- [Mesh Analysis - Blender Manual](https://docs.blender.org/manual/en/latest/modeling/meshes/mesh_analysis.html)
|
||||
- [When should N-Gons be used, and when shouldn't they?](https://blender.stackexchange.com/questions/89/when-should-n-gons-be-used-and-when-shouldnt-they)
|
||||
- [Exporting Blender models for Godot](https://tam7t.com/blender-godot-export)
|
||||
|
||||
## Non-coplanar Faces
|
||||
The first problem happens when during the process of creating a mesh an artist ends up with faces that are not coplanar.
|
||||
This means that the vertices of the face are not all on the same plane.
|
||||
For a triangle, the vertices are always on the same plane, but for quads and n-gons that is not necessarily true.
|
||||
|
||||
When a mesh has non-coplanar faces, the resulting triangulated mesh looks different than the original.
|
||||
|
||||
For a simple example, take a look at Blenders' Suzanne (this mesh has almost 90% non-coplanar faces) with and without triangulation
|
||||
|
||||
Suzanne without triangulation | Suzanne with `Triangulate` modifier
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
|
||||
Blender has tools to detect this type of problems in the mesh analysis section of the Viewport Overlays.
|
||||
|
||||
Select the object, then go into `Edit Mode` and in the upper right corner, there is an `Overlays` menu.
|
||||
|
||||

|
||||
|
||||
Select `Mesh Analysis` and then Type: `Distortion`
|
||||
|
||||
Blender will show all the faces that are non-coplanar and how much they deviate from a planar face.
|
||||
|
||||

|
||||
|
||||
Blender also has a tool to help with this issue.
|
||||
|
||||
While in `Edit Mode`, select Mesh -> Clean Up -> Make Planar Faces
|
||||
|
||||
It might be necessary to perform this action many times since it is a progressive improvement if there are many non-coplanar faces.
|
||||
|
||||
So increasing the factor and the iterations might help, the factor goes from [-10, 10] and the iterations go from [1, 10.000]
|
||||
|
||||
```python
|
||||
import bpy
|
||||
|
||||
bpy.ops.mesh.face_make_planar(factor=1, repeat=10)
|
||||
```
|
||||
|
||||
### Shape Keys
|
||||
|
||||
An issue caused by having non-coplanar faces is that since the triangulated mesh looks different from the original, the shape keys will tend to look more like the original mesh.
|
||||
That is because the data that Blender is providing to the exporter (particularly the normals) is not triangulated because the modifier does not apply to the Shape Keys.
|
||||
The result is that when using the Shape Keys or Shape Animations in OGRE the mesh changes in unintended ways.
|
||||
|
||||
In the following example, we created a Shape Key where Suzanne is worried.
|
||||
When applying the Shape Key in OGRE the normals are that of the original mesh and so it "looks" like the triangulation is gone.
|
||||
|
||||
Suzanne with `Triangulate` modifier | Suzanne in "worried" pose
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
|
||||
The fix is to avoid using non-coplanar faces or trying to fix them when they appear as a result of the modeling process.
|
||||
|
||||
|
||||
## Custom Normals
|
||||
Bugs reported to https://developer.blender.org
|
||||
- [Triangulate modifier breaks custom normals](https://developer.blender.org/T61942)
|
||||
- [Triangulate faces sometimes produce degenerate triangle faces](https://developer.blender.org/T103913)
|
||||
- [Triangulate non-planar faces with "beauty" method produces faces that point in different directions](https://developer.blender.org/T85402)
|
||||
- [Shading is broken with Custom Normals + Triangulate Modifier](https://developer.blender.org/T104244)
|
||||
|
||||
There are outstanding issues when using custom normals and Blenders' mesh triangulation, this is not only relegated to mesh triangulation but other tools as well.
|
||||
|
||||
So be careful when using Custom Normals (that is modifying normas by using some Modifier like Weighted Normal or directly editing the Normals in `Edit Mode`).
|
||||
|
||||
One way to check if the exported object will look good is to add `Triangulate` modifier with quad method `Fixed`, if the shading looks wrong compared to removing the modifier then it will look wrong in OGRE as well since the exporter is using Blenders triangulation code to perform the calculations.
|
||||
|
||||
For example, this cube has had a `Bevel` modifier and a `Weighted Normals` modifier applied, then we add a `Triangulate` modifier and the result is that there are visible triangles in the shading of the cube.
|
||||
|
||||
Cube without triangulation | Cube with triangulation
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
|
||||
> NOTE: Until this is fixed, for the Fillet Edges to look correct in the exported Mesh you have to create a copy of the object, apply the modifiers then go into `Edit Mode` and perform a manual triangulation of the Mesh (Face -> Triangulate Faces or Ctrl-T)
|
||||
|
||||
105
assets/blender/scripts/blender2ogre/Modifiers.md
Normal file
@@ -0,0 +1,105 @@
|
||||
|
||||
# Blender Modifiers
|
||||
Modifiers are automatic operations that affect an object’s geometry in a non-destructive way.
|
||||
With modifiers, you can perform many effects automatically that would otherwise be too tedious to do manually
|
||||
(such as subdivision surfaces) and without affecting the base geometry of your object.
|
||||
|
||||
`blender2ogre` supports exporting meshes with modifiers, but not all modifiers are supported.
|
||||
Also, some modifiers have special treatment (Array and Armature), please check the corresponding sections
|
||||
|
||||
> NOTE: Support for modifiers is *best effort*, in most cases the modifiers have been tested individually and not all combinations have been tried.
|
||||
|
||||
> **WARNING**: Beware of using Modifiers that increase the vertex o poly count of the models when exporting (like Subdivision Surface) since the exported mesh might not be very optimal for realtime rendering. Retopology is advised in these cases to improve render times.
|
||||
|
||||
## Index
|
||||
- [Documentation](#documentation)
|
||||
- [Modify type Modifiers](#modify-type-modifiers)
|
||||
- [Generate type Modifiers](#generate-type-modifiers)
|
||||
- [Deform type Modifiers](#deform-type-modifiers)
|
||||
- [Array Modifier](#array-modifier)
|
||||
- [Boolean Modifier](#boolean-modifier)
|
||||
|
||||
## Documentation
|
||||
- [Modifiers - Introduction; Blender Manual](https://docs.blender.org/manual/en/latest/modeling/modifiers/introduction.html)
|
||||
|
||||
## Modify type Modifiers
|
||||
Modifier | Supported | Notes
|
||||
:-------:|:---------:|:----:
|
||||
[Data Transfer](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/data_transfer.html) |  | Mesh exports OK, with normals properly modified
|
||||
[Mesh Cache](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/mesh_cache.html) |  | Mesh exports OK, but the exported mesh won't be animated. Please check [XXX] to see how to bake Animations
|
||||
[Mesh Sequence Cache](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/mesh_sequence_cache.html) |  | Mesh exports OK, but the exported mesh won't be animated. Please check [XXX] to see how to bake Animations
|
||||
[Normal Edit](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/normal_edit.html) |  | Mesh exports OK, with normals properly modified
|
||||
[UV Project](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/uv_project.html) |  | Mesh exports OK and UV Maps are projected, although any UV animations made in Blender won't be exported and are not supported in OGRE.
|
||||
[UV Warp](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/uv_warp.html) |  | Mesh exports OK and UV Maps are warped, although any UV animations made in Blender won't be exported and are not supported in OGRE.
|
||||
[Vertex Weight Edit](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/weight_edit.html) |  | Mesh exports OK, with normals properly modified
|
||||
[Vertex Weight Mix](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/weight_mix.html) |  | Mesh exports OK, with normals properly modified
|
||||
[Vertex Weight Proximity](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/weight_proximity.html) |  | Mesh exports OK, but it does not do animation like the Blender example shows
|
||||
[Weighted Normals](https://docs.blender.org/manual/en/latest/modeling/modifiers/modify/weighted_normal.html) |  | Mesh exports OK, but affected by [Blenders' triangulation bug](MeshTriangulation.md)
|
||||
|
||||
## Generate type Modifiers
|
||||
Modifier | Supported | Notes
|
||||
:-------:|:---------:|:----:
|
||||
[Array](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/array.html) |  | Has full support: [Array Modifier](#array-modifier)
|
||||
[Bevel](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/bevel.html) |  | Mesh exports OK with bevel
|
||||
[Boolean](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/booleans.html) |  | Mesh exports OK with boolean operation applied, but check [Boolean Modifier](#boolean-modifier) section for more information
|
||||
[Build](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/build.html) |  | Can't export a mesh with varying vertex count
|
||||
[Decimate](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/decimate.html) |  | Mesh exports OK properly decimated
|
||||
[Edge Split](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/edge_split.html) |  | Mesh exports OK with modified normals
|
||||
[Geometry Nodes](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/geometry_nodes.html) |  | Mesh exports OK with the proper geometry
|
||||
[Mask](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/mask.html) |  | Mesh exports OK with applied mask
|
||||
[Mirror](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/mirror.html) |  | Mesh exports OK with applied mirroring
|
||||
[Multiresolution](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/multiresolution.html) |  | Mesh exports OK, the `Level Viewport` parameter should be more than 0, otherwise the base mesh will be exported. Also, this potentially exports an insane amount of geometry, you might want to do a retopology and use normal maps to bake the details.
|
||||
[Remesh](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/remesh.html) |  | Mesh exports OK, but UV Maps are removed from the Mesh
|
||||
[Screw](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/screw.html) |  | Mesh exports OK, but the base object has to be a mesh otherwise nothing is exported
|
||||
[Skin](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/skin.html) |  | Mesh exports OK, but UV Maps are removed from the Mesh
|
||||
[Solidify](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/solidify.html) |  | Mesh exports OK with thickness added
|
||||
[Subdivision Surface](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/subdivision_surface.html) |  | Mesh exports OK, as with the `Multiresolution Modifier` beware of the vertex count of the exported mesh (which affects performance).
|
||||
[Triangulate](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/triangulate.html) |  | Mesh exports OK, but affected by [Blenders' triangulation bug](MeshTriangulation.md)
|
||||
[Volume to Mesh](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/volume_to_mesh.html) |  | Mesh exports OK, but UV Maps are removed from the Mesh
|
||||
[Weld Modifier](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/weld.html) |  | Mesh exports OK
|
||||
[Wireframe](https://docs.blender.org/manual/en/latest/modeling/modifiers/generate/wireframe.html) |  | Mesh exports OK
|
||||
|
||||
## Deform type Modifiers
|
||||
Modifier | Supported | Notes
|
||||
:-------:|:---------:|:----:
|
||||
[Armature](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/armature.html) |  | Has full support: [Exporting Skeletal Animations](SkeletalAnimation.md)
|
||||
[Cast](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/cast.html) |  | Mesh exports OK
|
||||
[Curve](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/curve.html) |  | Mesh exports OK
|
||||
[Displace](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/displace.html) |  | Mesh exports OK
|
||||
[Hook](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/hooks.html) |  | Mesh exports OK
|
||||
[Laplacian Deform](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/laplacian_deform.html) |  | Mesh exports OK
|
||||
[Lattice](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/lattice.html) |  | Mesh exports OK
|
||||
[Mesh Deform](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/mesh_deform.html) |  | Mesh exports OK
|
||||
[Shrinkwrap](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/shrinkwrap.html) |  | Mesh exports OK
|
||||
[Simple Deform](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/simple_deform.html) |  | Mesh exports OK
|
||||
[Smooth](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/smooth.html) |  | Mesh exports OK
|
||||
[Smooth Laplacian](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/laplacian_smooth.html) |  | Mesh exports OK
|
||||
[Surface Deform](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/surface_deform.html) |  | Mesh exports OK
|
||||
[Volume Displace](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/volume_displace.html) |  | Only works on volumes, not meshes
|
||||
[Warp](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/warp.html) |  | Mesh exports OK
|
||||
[Wave](https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/wave.html) |  | Mesh exports OK, but only in the first frame there is no motion. To bake the animation, consult [xxx]
|
||||
|
||||
## Array Modifier
|
||||
This modifier as well as the `Armature Modifier` get their special section because they are treated differently by `blender2ogre`.
|
||||
Most modifiers are applied before exporting the model (without affecting the object) by converting an evaluated copy of the object into a mesh.
|
||||
|
||||
However the case of the `Array Modifier` is different, since the presence of this modifier has `blender2ogre` treat the object differently.
|
||||
|
||||
What happens is that in `scene.py` (which creates the .scene output) the `Array Modifier` is used to place instances of the mesh in positions indicated by the modifier.
|
||||
This means that the exported mesh appearance is not modified by the `Array Modifier`, but only its placement in the scene.
|
||||
|
||||
As a result, there is only one copy of the mesh in the scene that is repeated many times, which could lead to a performance increase if using instancing.
|
||||
|
||||
> NOTE: To disable this behavior and have the `Array Modifier` be applied to the mesh directly, then set the option: `ARRAY` to true in the mesh options
|
||||
|
||||
## Boolean Modifier
|
||||
This modifier works well and is in principle fully supported, but you might get this error when exporting meshes with the `Boolean Modifier`:
|
||||
```
|
||||
FAILED to assign material to face - you might be using a Boolean Modifier between objects with different materials! [ mesh : Cube ]
|
||||
```
|
||||
|
||||
The issue here is that `blender2ogre` has a problem when the two objects that make contact to perform the boolean operation don't have the same material assigned to the faces which enter into contact.
|
||||
|
||||
To solve this you need to assign the same material to the faces which are in contact, this might be as simple as assigning a Material to the whole secondary object or having to do something more complex like assigning the same material to the faces that come into contact by entering `Edit Mode` and assigning the material by hand to each face.
|
||||
|
||||
As a last resort, it is also possible to make a copy by hand of the object by applying the `Boolean Modifier` and exporting that mesh.
|
||||
209
assets/blender/scripts/blender2ogre/NodeAnimations.md
Normal file
@@ -0,0 +1,209 @@
|
||||
|
||||
# Exporting Node Animations
|
||||
|
||||
## Introduction
|
||||
It is possible to export some animated properties of objects in Blender, that is location, rotation and scale.
|
||||
|
||||
This results in what is called SceneNode Animation (https://ogrecave.github.io/ogre/api/latest/_animation.html#SceneNode-Animation) in Ogre.
|
||||
|
||||
This kind of SceneNode animation is very useful to animate the movement of a camera, a character patrolling in a game and any other kind of animation one can think of.
|
||||
|
||||
The advantage over skeletal animation is that it is more performant due to its simplicity, and the effect (translation/rotation/scale) is applied to the Node and its children (if `setInherit()` is true).
|
||||
|
||||
## About exporting and importing into Ogre
|
||||
The exported animation data goes into the .scene file describing the whole Scene crafted in Blender, there isn't a serializer for SceneNode Animation data, so this data has to be processed by the DotScene plugin.
|
||||
|
||||
Refer to the DotScene documentation (https://github.com/OGRECave/ogre/tree/master/PlugIns/DotScene) on how to load the .scene file into your Ogre application.
|
||||
|
||||
The animation is being exported frame by frame with Blender doing the interpolation between keyframes.
|
||||
This has the advantage that any tuning done to the F-Curves is preserved in the exported animation.
|
||||
Another advantage is that it is not necessary to choose an interpolation method other than IM_LINEAR on the Ogre side, making the animation more performant.
|
||||
|
||||
The disadvantage is that the .scene file is now less human-readable due to the chunk of data from all the animation frames and there is no control of the animation in the code.
|
||||
Another disadvantage is that the timing of the frames will be interpreted according to Blender's current FPS setting which is by default 24 fps.
|
||||
This setting can be changed in the `Render Tab`, `Dimensions Panel`, there is a drop-down list named `Frame Rate` where you can select a value or create a new one with custom.
|
||||
|
||||
In a similar fashion to the exporting of Skeletal Animation, every action should go into an NLA Track to have it exported (the name of the exported animation being that of the action).
|
||||
|
||||
The reason for using NLA Tracks is to have better control over what actions should be exported as animations.
|
||||
|
||||
## Animating an object
|
||||
Here is a short video on how to animate objects in Blender:
|
||||
[How To Move Animated Objects (Blender Tutorial)](https://www.youtube.com/watch?v=HPD3LhCLxCE)
|
||||
|
||||
We will use the default cube to simplify things.
|
||||
|
||||
Just select the cube and toggle the `Transform` properties with the keyboard shortcut `N`.
|
||||
|
||||

|
||||
|
||||
Make sure that the current frame is `1` and with the mouse cursor over one of the `Location` properties, press `I`.
|
||||
|
||||
This has the effect of automatically creating an action called `CubeAction` and inserting a Keyframe at frame `1`.
|
||||
|
||||
Do the same for `Rotation` and `Scale`.
|
||||
|
||||
Then jump to frame `30` and change `Location.Z` to `2m`, `Rotation.Z` to `45º` and `Scale.XYZ` to `1.5`.
|
||||
|
||||
Hover the mouse cursor on `Location`, `Rotation` and `Scale` pressing `I` to insert a Keyframe at frame `30`.
|
||||
|
||||

|
||||
|
||||
Play the animation and you will see the cube go upwards, increase in size 1.5 times and rotate 45º degrees to the right.
|
||||
|
||||
Don't forget to `Push Down` the action from the `Action Editor` to put it into the NLA stack before exporting.
|
||||
|
||||

|
||||
|
||||
## Having an object follow a path
|
||||
Here is a short video on how to have an object follow a path in Blender
|
||||
[Create Path For Any Object To Follow - Blender (BEGINNERS)](https://www.youtube.com/watch?v=G6NdQGySZhU)
|
||||
|
||||
### Simple Path Following
|
||||
For this simple tutorial, we will use the default cube to follow a path in Blender.
|
||||
|
||||
Create a new scene and select the default cube.
|
||||
|
||||
Press `Shift-A` to open the `Add` menu and add a circle (`Curve -> Circle`).
|
||||
|
||||
Scale the circle to four times its size by pressing `S` and then move the mouse to increase the size of the circle so you have a decent-sized path.
|
||||
|
||||
Now, select the cube and go to the `Constraints` tab and click on `Add Object Constraint` to add a new constraint.
|
||||
|
||||
Select the `Follow Path` constraint to have the cube following the circle as a path.
|
||||
|
||||

|
||||
|
||||
Now, set the "BezierCircle" as the `Target`, click on `Follow Curve` and `Fixed Position`.
|
||||
|
||||
With meshes not as symmetrical as the cube you might have to fiddle with the `Forward Axis` to have the meshes front facing the direction of the path.
|
||||
|
||||
To have an animation of the cube following the path, hover the mouse over the `Offset` slider and press `I` to insert a Keyframe at frame 1.
|
||||
|
||||
Select frame 60 and change the value of the `Offset` slider to 1.0 and press `I` to insert a Keyframe at frame 60.
|
||||
|
||||
The cube follows the path but the speed is not constant, to have a constant speed it is necessary to modify the `F-Curve` and set it to linear interpolation.
|
||||
|
||||
To do this, go to the `Graph Editor` and select `Key->Interpolation Mode->Linear`, now the cube will follow the path at a constant speed.
|
||||
|
||||
To have a path that is more sinusoidal than circular it is possible to also use a `Bezier Curve` as the `Target` of the `Follow Path` constraint.
|
||||
|
||||
If you want this path to loop on itself press `Alt-C` when editing the `Bezier Curve` in edit mode (also `E` adds more segments to the curve).
|
||||
|
||||
To export a .scene with the animated cube following the path, the last step is to add an NLA Track so that the exporter knows which action to export.
|
||||
|
||||
Go to the `Dope Sheet`, change the mode to `Action Editor` and select `Push Down` to add the action to the NLA stack.
|
||||
|
||||

|
||||
|
||||
### Path Following with Skeletal Animation
|
||||
To have an animated character follow a path there are a couple of things to take into account.
|
||||
|
||||
It is very important to parent the animated character armature to an empty object.
|
||||
Add the empty object with `Shift-A->Empty->Plain Axes`, then select the armature and afterwards select the empty by pressing `Shift` to have both objects selected in the proper order.
|
||||
Press `Ctrl-P` to get the `Parent` menu and select `Object (Keep Transform)`.
|
||||
Having the Armature being a child of the empty object allows us to have the path following be independent of the Armature's actions.
|
||||
|
||||
The second advantage of having the Armature parented to an empty is that it is possible to control the scale of the animated character without breaking the animations.
|
||||
|
||||
Just set the desired scale of the empty object and the armature will inherit that scale, the exported .scene will have an Ogre SceneNode with the name of the empty as a parent of the Armature SceneNode.
|
||||
|
||||
### Path Following with Camera Tracking
|
||||
To have a camera following a path and tracking a target we will be combining the things learned in the previous sections.
|
||||
|
||||
Create a new scene and delete the default cube.
|
||||
|
||||
Add three empty objects (3 x `Shift-A->Empty->Plain Axes`) and name them as follows: *Tracked*, *CameraBase* and *CameraArm*.
|
||||
|
||||
Parent the empty *CameraArm* to *CameraBase* so that the arm is a child of the base, and then parent the camera to *CameraArm*.
|
||||
|
||||
Clear the camera location and rotation by pressing `Alt-G`, `Alt-R`.
|
||||
|
||||
Add a Monkey mesh with `Shift-A->Mesh->Monkey` to have some objects for the camera to look at.
|
||||
|
||||
Then add a BezierCircle to have a path to follow, scale it and name it *CameraPath*.
|
||||
|
||||
Select the *CameraBase* empty and add a constraint to follow the *CameraPath* bezier curve with `Forward:` -X and `Up:` Z (also check `Follow Curve` and `Fixed Position`).
|
||||
|
||||
Select the *CameraArm* empty and move it upwards so that the camera is looking at the object from a vantage point and not the floor.
|
||||
|
||||
Then select the camera and add a constraint of type `Locked Track`, with `Target:` *Tracked*, `To:` -Z and `Lock:` X.
|
||||
|
||||
The purpose of having the camera track the *Tracked* empty object instead of the Monkey mesh is that now it is not locked to the center of the object.
|
||||
|
||||
If the Monkey object is going to move as well, then the *Tracked* empty object can be made a child of the Monkey object or use more empty objects to move the monkey.
|
||||
|
||||
It is a good general rule to separate movements into independent nodes as was done with *CameraBase* and *CameraArm* which are generally used to separate the yaw and pitch of the camera.
|
||||
|
||||
Now, select *CameraBase* and repeat the process as in the previous tutorial to have the *CameraBase* empty follow the circular path, in the process the camera will keep on tracking the *Tracked* empty object.
|
||||
|
||||
It should be noted that Ogre has a `setAutoTracking()` function for the SceneNodes, which would be a preferable way to track objects.
|
||||
|
||||
Please consult the manual for more details: https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_node.html#ae4c8588895d3623bbe0007ea157af1a4
|
||||
|
||||
## Using Node Animations in your Ogre app
|
||||
If you are using DotScene to load the .scene into your Ogre app, by default the animations won't play because they are disabled by default.
|
||||
|
||||
Besides enabling the animations it is also required to `addTime()` to their *AnimationStates*, we will use a controller for that.
|
||||
|
||||
```
|
||||
int main()
|
||||
{
|
||||
...
|
||||
auto& controllerMgr = Ogre::ControllerManager::getSingleton();
|
||||
|
||||
for (auto animationState : mSceneMgr->getAnimationStates())
|
||||
{
|
||||
// Print the animation name
|
||||
std::cout << "animationState: " << animationState.second->getAnimationName() << std::endl;
|
||||
|
||||
// Enable the Animation State (they are disabled by default)
|
||||
animationState.second->setEnabled(true);
|
||||
|
||||
// Set the animation to looping (if your node animation loops)
|
||||
animationState.second->setLoop(true);
|
||||
|
||||
// Create a controller to pass the frame time to the Animation State, otherwise the animation won't play
|
||||
// (this is a better method than using animationState->addTime() in your main loop)
|
||||
controllerMgr.createFrameTimePassthroughController(Ogre::AnimationStateControllerValue::create(animationState.second, true));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Consult the Ogre manual for further information:
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_manager.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_animation_state.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_node.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_controller_manager.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_controller.html
|
||||
|
||||
## Troubleshooting
|
||||
Some tips for troubleshooting:
|
||||
- Make sure that the actions/animations you want to export have their corresponding NLA tracks associated with the object.
|
||||
- To use Skeletal Animation combined with the path following remember to have the armature be a child of an empty to avoid problems.
|
||||
- Check the .scene file to see if the animation was exported as expected, the problem might not be in the exported data.
|
||||
|
||||
## Automation
|
||||
If you have many actions it can become tiresome to push every one of them into the NLA stack to have them exported.
|
||||
|
||||
Here is a very simple Blender script to add every action as an NLA track, any action which should not be exported can simply be deleted from the list of NLA Tracks
|
||||
|
||||
Switch to the `Scripting` layout and copy the following script:
|
||||
|
||||
```
|
||||
import bpy
|
||||
|
||||
def add_NLA_strips(object):
|
||||
|
||||
for action in bpy.data.actions:
|
||||
track = bpy.data.objects[object].animation_data.nla_tracks.new()
|
||||
track.strips.new(action.name, action.frame_range[0], action)
|
||||
track.name = action.name
|
||||
|
||||
# Add actions from a single object:
|
||||
add_NLA_strips('Cube')
|
||||
|
||||
# Add actions for every object:
|
||||
for object in bpy.data.objects:
|
||||
add_NLA_strips(object)
|
||||
```
|
||||
219
assets/blender/scripts/blender2ogre/Options.md
Normal file
@@ -0,0 +1,219 @@
|
||||
|
||||
# `blender2ogre` Options
|
||||
|
||||
## Index
|
||||
- [Exporter](#exporter)
|
||||
- [Exporter Options](#exporter-options)
|
||||
- [Exporter Script](#exporter-script)
|
||||
- [Importer](#importer)
|
||||
- [Importer Options](#importer-options)
|
||||
- [Importer Script](#importer-script)
|
||||
|
||||
## Exporter
|
||||
|
||||
### Exporter Options
|
||||
Option|Name|Description|Default Value
|
||||
|---|---|---|---|
|
||||
|**General**|
|
||||
|EX_SWAP_AXIS|Swap Axis|Axis swapping mode|'xyz'|
|
||||
|EX_V2_MESH_TOOL_VERSION|Mesh Export Version|Specify Ogre version format to write|'v2'|
|
||||
|EX_XML_DELETE|Clean up xml files|Remove the generated xml files after binary conversion.[^1]|True|
|
||||
|**Scene**|
|
||||
|EX_SCENE|Export Scene|Export current scene (OgreDotScene xml file)|True|
|
||||
|EX_SELECTED_ONLY|Export Selected Only|Export only selected objects. Turn on to avoid exporting non-selected stuff|True|
|
||||
|EX_EXPORT_HIDDEN|Export Hidden Also|Export hidden meshes in addition to visible ones. Turn off to avoid exporting hidden stuff|True|
|
||||
|EX_EXPORT_USER|Export User Properties|Export user properties such as as physical properties. Turn off to avoid exporting the user data|True|
|
||||
|EX_FORCE_CAMERA|Force Camera|Export active camera|True|
|
||||
|EX_FORCE_LAMPS|Force Lamps|Export all Lamps|True|
|
||||
|EX_NODE_ANIMATION|Export Node Animations|Export Node Animations, these are animations of the objects properties like position, rotation and scale|True|
|
||||
|**Materials**|
|
||||
|EX_MATERIALS|Export Materials|Exports .material scripts|True|
|
||||
|EX_SEPARATE_MATERIALS|Separate Materials|Exports a .material for each material (rather than putting all materials into a single .material file)|True|
|
||||
|EX_COPY_SHADER_PROGRAMS|Copy Shader Programs|When using script inheritance copy the source shader programs to the output path|True|
|
||||
|EX_USE_FFP_PARAMETERS|Fixed Function Parameters|Convert material parameters to Blinn-Phong model|False|
|
||||
|**Textures**|
|
||||
|EX_DDS_MIPS|DDS Mips|Number of Mip Maps (DDS)|16|
|
||||
|EX_FORCE_IMAGE_FORMAT|Convert Images|Convert all textures to selected image format|'NONE'|
|
||||
|**Armature**|
|
||||
|EX_ARMATURE_ANIMATION|Armature Animation|Export armature animations (updates the .skeleton file)|True|
|
||||
|EX_SHARED_ARMATURE|Shared Armature|Export a single .skeleton file for objects that have the same Armature parent[^2]|False|
|
||||
|EX_ONLY_KEYFRAMES|Only Keyframes|Only export Keyframes.[^3]|False|
|
||||
|EX_ONLY_DEFORMABLE_BONES|Only Deformable Bones|Only exports bones that are deformable.[^4]|False|
|
||||
|EX_ONLY_KEYFRAMED_BONES|Only Keyframed Bones|Only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis|False|
|
||||
|EX_OGRE_INHERIT_SCALE|OGRE Inherit Scale|Whether the OGRE bones have the 'inherit scale' flag on.[^5]|False|
|
||||
|EX_TRIM_BONE_WEIGHTS|Trim Weights|Ignore bone weights below this value (Ogre supports 4 bones per vertex)|0.01|
|
||||
|**Mesh Options**|
|
||||
|EX_MESH|Export Meshes|Export meshes|True|
|
||||
|EX_MESH_OVERWRITE|Export Meshes (overwrite)|Export meshes (overwrite existing files)|True|
|
||||
|EX_ARRAY|Optimise Arrays|Optimise array modifiers as instances (constant offset only)|True|
|
||||
|EX_V1_EXTREMITY_POINTS|Extremity Points|[^6]|0|
|
||||
|EX_Vx_GENERATE_EDGE_LISTS|Generate Edge Lists|Generate Edge Lists (for Stencil Shadows)|False|
|
||||
|EX_GENERATE_TANGENTS|Tangents|Export tangents generated by Blender[^7]|0|
|
||||
|EX_Vx_OPTIMISE_ANIMATIONS|Optimise Animations|DON"T optimise out redundant tracks & keyframes|True|
|
||||
|EX_V2_OPTIMISE_VERTEX_BUFFERS|Optimise Vertex Buffers For Shaders|Optimise vertex buffers for shaders.[^8]|True|
|
||||
|EX_V2_OPTIMISE_VERTEX_BUFFERS_OPTIONS|Vertex Buffers Options|Used when optimizing vertex buffers for shaders.[^9]|'puqs'|
|
||||
|**LOD**|
|
||||
|EX_LOD_LEVELS|LOD Levels|Number of LOD levels|0|
|
||||
|EX_LOD_DISTANCE|LOD Distance|Distance increment to reduce LOD|300|
|
||||
|EX_LOD_PERCENT|LOD Percentage|LOD percentage reduction|40|
|
||||
|EX_LOD_MESH_TOOLS|Use OgreMesh Tools|Use OgreMeshUpgrader/OgreMeshTool instead of Blender to generate the mesh LODs.[^10]|False|
|
||||
|**Pose Animation**|
|
||||
|EX_SHAPE_ANIMATIONS|Shape Animation|Export shape animations (updates the .mesh file)|True|
|
||||
|EX_SHAPE_NORMALS|Shape Normals|Export normals in shape animations (updates the .mesh file)|True|
|
||||
|**Logging**|
|
||||
|EX_Vx_ENABLE_LOGGING|Write Exporter Logs|Write Log file to the output directory (blender2ogre.log)|False|
|
||||
|EX_Vx_DEBUG_LOGGING|Debug Logging|Whether to show DEBUG log messages|False|
|
||||
|
||||
[^1]: The removal will only happen if OgreXMLConverter/OgreMeshTool finishes successfully
|
||||
[^2]: This is useful for using with: `shareSkeletonInstanceWith()`
|
||||
NOTE: The name of the.skeleton file will be that of the Armature
|
||||
[^3]: Exported animation won't be affected by Inverse Kinematics, Drivers and modified F-Curves
|
||||
[^4]: Useful for hiding IK-Bones used in Blender.
|
||||
NOTE: Any bone with deformablechildren/descendants will be output as well
|
||||
[^5]: If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE
|
||||
[^6]: Submeshes can have optional "extremity points" stored with them to allow submeshes to be sorted with respect to each other in the case of transparency.
|
||||
For some meshes with transparent materials (partial transparency) this can be useful
|
||||
[^7]: Options:
|
||||
'0': Do not export
|
||||
'3': Generate
|
||||
'4': Generate with parity
|
||||
[^8]: See Vertex Buffers Options for more settings
|
||||
[^9]: Available flags are:
|
||||
p - converts POSITION to 16-bit floats.
|
||||
q - converts normal tangent and bitangent (28-36 bytes) to QTangents (8 bytes).
|
||||
u - converts UVs to 16-bit floats.
|
||||
s - make shadow mapping passes have their own optimised buffers. Overrides existing ones if any.
|
||||
S - strips the buffers for shadow mapping (consumes less space and memory)
|
||||
[^10]: OgreMeshUpgrader/OgreMeshTool does LOD by removing edges, which allows only changing the index buffer and re-use the vertex-buffer (storage efficient).
|
||||
Blenders decimate does LOD by collapsing vertices, which can result in a visually better LOD, but needs different vertex-buffers per LOD.
|
||||
|
||||
### Exporter Script
|
||||
This is an example exporting script with all the options and their default values
|
||||
|
||||
```python
|
||||
import bpy
|
||||
|
||||
bpy.ops.ogre.export(
|
||||
filepath="D:\\tmp\\NormalsExport\\blender2ogre.scene",
|
||||
|
||||
# General
|
||||
EX_SWAP_AXIS='xz-y',
|
||||
# - 'xyz': No swapping
|
||||
# - 'xz-y'OGRE Standard
|
||||
# - '-xzy': Non standard
|
||||
EX_V2_MESH_TOOL_VERSION='v2',
|
||||
# - 'v1': Export the mesh as a v1 object
|
||||
# - 'v2': Export the mesh as a v2 object
|
||||
EX_XML_DELETE=True,
|
||||
|
||||
# Scene
|
||||
EX_SCENE=True,
|
||||
EX_SELECTED_ONLY=True,
|
||||
EX_EXPORT_HIDDEN=True,
|
||||
EX_FORCE_CAMERA=True,
|
||||
EX_FORCE_LAMPS=True,
|
||||
EX_NODE_ANIMATION=True,
|
||||
|
||||
# Materials
|
||||
EX_MATERIALS=True,
|
||||
EX_SEPARATE_MATERIALS=True,
|
||||
EX_COPY_SHADER_PROGRAMS=True,
|
||||
|
||||
# Textures
|
||||
EX_DDS_MIPS=16,
|
||||
EX_FORCE_IMAGE_FORMAT='NONE',
|
||||
|
||||
# Armature
|
||||
EX_ARMATURE_ANIMATION=True,
|
||||
EX_SHARED_ARMATURE=False,
|
||||
EX_ONLY_KEYFRAMES=False,
|
||||
EX_ONLY_DEFORMABLE_BONES=False,
|
||||
EX_ONLY_KEYFRAMED_BONES=False,
|
||||
EX_OGRE_INHERIT_SCALE=False,
|
||||
EX_TRIM_BONE_WEIGHTS=0.01,
|
||||
|
||||
# Mesh Options
|
||||
EX_MESH=True,
|
||||
EX_MESH_OVERWRITE=True,
|
||||
EX_ARRAY=True,
|
||||
EX_V1_EXTREMITY_POINTS=0,
|
||||
EX_Vx_GENERATE_EDGE_LISTS=False,
|
||||
EX_GENERATE_TANGENTS='0',
|
||||
# - '0': Do not export
|
||||
# - '3': Generate
|
||||
# - '4': Generate with parity
|
||||
EX_Vx_OPTIMISE_ANIMATIONS=True,
|
||||
EX_V2_OPTIMISE_VERTEX_BUFFERS=True,
|
||||
EX_V2_OPTIMISE_VERTEX_BUFFERS_OPTIONS="puqs",
|
||||
|
||||
# LOD
|
||||
EX_LOD_LEVELS=0,
|
||||
EX_LOD_DISTANCE=300,
|
||||
EX_LOD_PERCENT=40,
|
||||
EX_LOD_MESH_TOOLS=False,
|
||||
|
||||
# Pose Animation
|
||||
EX_SHAPE_ANIMATIONS=True,
|
||||
EX_SHAPE_NORMALS=True,
|
||||
|
||||
# Logging
|
||||
EX_Vx_ENABLE_LOGGING=True,
|
||||
EX_Vx_DEBUG_LOGGING=True
|
||||
)
|
||||
```
|
||||
|
||||
## Importer
|
||||
|
||||
### Importer Options
|
||||
Option|Name|Description|Default Value
|
||||
|---|---|---|---|
|
||||
|**General**|
|
||||
|IM_SWAP_AXIS|Swap Axis|Axis swapping mode|'xyz'|
|
||||
|IM_V2_MESH_TOOL_VERSION|Mesh Import Version|Specify Ogre version format to read|'v2'|
|
||||
|IM_XML_DELETE|Clean up xml files|Remove the generated xml files after binary conversion.[^11]|True|
|
||||
|**Mesh**|
|
||||
|IM_IMPORT_NORMALS|Import Normals|Import custom mesh normals|True|
|
||||
|IM_MERGE_SUBMESHES|Merge Submeshes|Whether to merge submeshes to form a single mesh with different materials|True|
|
||||
|**Armature**|
|
||||
|IM_IMPORT_ANIMATIONS|Import animation|Import animations as actions|True|
|
||||
|IM_ROUND_FRAMES|Adjust frame rate|Adjust scene frame rate to match imported animation|True|
|
||||
|IM_USE_SELECTED_SKELETON|Use selected skeleton|Link with selected armature object rather than importing a skeleton.[^12]|True|
|
||||
|**Shape Keys**|
|
||||
|IM_IMPORT_SHAPEKEYS|Import shape keys|Import shape keys (morphs)|True|
|
||||
|**Logging**|
|
||||
|IM_Vx_ENABLE_LOGGING|Write Importer Logs|Write Log file to the output directory (blender2ogre.log)|False|
|
||||
|
||||
[^11]: The removal will only happen if OgreXMLConverter/OgreMeshTool finishes successfully
|
||||
[^12]: Use this for importing skinned meshes that don't have their own skeleton.
|
||||
Make sure you have the correct skeleton selected or the weight maps may get mixed up.
|
||||
|
||||
### Importer Script
|
||||
This is an example importing script with all the options and their default values
|
||||
|
||||
```python
|
||||
import bpy
|
||||
|
||||
bpy.ops.ogre.import_mesh(
|
||||
filepath="D:\\tmp\\NormalsExport\\Suzanne.mesh",
|
||||
|
||||
# General
|
||||
IM_SWAP_AXIS='xz-y', # Axis swapping mode
|
||||
IM_V2_MESH_TOOL_VERSION='v2', # Specify Ogre version format to read
|
||||
IM_XML_DELETE=True, # Remove the generated xml files after binary conversion.
|
||||
|
||||
# Mesh
|
||||
IM_IMPORT_NORMALS=True, # Import custom mesh normals
|
||||
IM_MERGE_SUBMESHES=True, # Whether to merge submeshes to form a single mesh with different materials
|
||||
|
||||
# Armature
|
||||
IM_IMPORT_ANIMATIONS=True, # Import animations as actions
|
||||
IM_ROUND_FRAMES=True, # Adjust scene frame rate to match imported animation
|
||||
IM_USE_SELECTED_SKELETON=True, # Link with selected armature object rather than importing a skeleton
|
||||
|
||||
# Shape Keys
|
||||
IM_IMPORT_SHAPEKEYS=True, # Import shape keys (morphs)
|
||||
|
||||
# Logging
|
||||
IM_Vx_ENABLE_LOGGING=True # Write Log file to the output directory (blender2ogre.log)
|
||||
)
|
||||
```
|
||||
124
assets/blender/scripts/blender2ogre/ParticleSystem.md
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
# Exporting Particle Systems
|
||||
|
||||
## Introduction
|
||||
A common technique for laying out random objects on a scene in Blender is to use the Particle System.
|
||||
(That is, before Geometry Nodes in Blender 2.92)
|
||||
|
||||
In this tutorial, we are going to explore how to lay out objects in a scene randomly and export that scene for use with OGRE.
|
||||
|
||||
This tutorial is based on the ideas shown in this "CG Geek" video:
|
||||
[Create Realistic Grass in Blender 2.8 in 15 minutes](https://www.youtube.com/watch?v=-GAm-7_3N6g)
|
||||
|
||||
Terminology:
|
||||
- *Base Object*: an object with a mesh that represents a terrain or some other form on which we want to place random objects on.
|
||||
- *Dupli Objects*: an object with a mesh that represents random objects (like grass, trees, rocks, etc.) that we want to place on the Base object.
|
||||
|
||||
The scene in the images is available [here](examples/particle-system.blend), it was made with the A.N.T Landscape Plugin that comes bundled with Blender (River preset) and very simple "Earth" and "Water" materials.
|
||||
|
||||
## Creating and setting up the Particle System
|
||||
Select the mesh where you want to place your foliage or debris (the *Base Object*).
|
||||
|
||||
Go to the `Particles` tab and click on new to add a new particle system.
|
||||
|
||||
Select type: `Hair` and click on `Advanced` to show the advanced options.
|
||||
|
||||
By default `Hair Length` is set to 4 which makes the debris show 4 x larger so set the value to 1.
|
||||
|
||||
With `Number` it is possible to control the number of objects that will appear randomly on the mesh surface.
|
||||
|
||||

|
||||
|
||||
Click on `Rotation` and select `Initial Orientation: Normal`.
|
||||
|
||||
You can change the `Phase` and `Random` values to have the debris show random rotations around the Z axis (tangent space), this way if you have trees they will be showing in different orientations.
|
||||
|
||||

|
||||
|
||||
In the `Render` setting change the type to `Object` and in `Dupli Object` select the object you want to duplicate randomly over the mesh.
|
||||
Check the `Rotation` and `Scale` options so the Particle System will take the *Dupli Objects* rotation and scale into account.
|
||||
Set `Size` to 1 and choose a `Random Size` value to control the amount of randomness in the size distribution of the *Dupli Objects*.
|
||||
|
||||

|
||||
|
||||
At this point, you should see the object appearing randomly in the places where there were hair strands.
|
||||
But, the objects appear rotated -90 degrees over the Y axis.
|
||||
To solve this it is necessary to rotate the Dupli Object 90 degrees over the Y-axis to counter this rotation.
|
||||
Don't apply this rotation to the Dupli Object, otherwise the `blender2ogre` add-on will export the mesh with this rotation applied.
|
||||
The add-on will automatically apply this rotation to the nodes in the exported scene.
|
||||
|
||||
Another thing to take into account is that the Dupli Object origin should be at its base, otherwise it will appear to be buried halfway.
|
||||
|
||||
## Extras
|
||||
It is also possible to paint certain parts of the Base Objects mesh to modify the *Dupli Objects* density by using weight painting.
|
||||
Select the *Base Object*, go into weight paint mode and start painting the areas where you want to increase the *Dupli Objects* density.
|
||||
|
||||
Remember that the *Base Object* should have some tesselation for this to work well, otherwise there might be few vertices to paint.
|
||||
One way to tesselate is to go into Edit mode and subdivide or activate Dynotopo in Sculpt mode.
|
||||
|
||||
After painting the areas go back to the `Particles` tab and in the `Vertex Groups` section choose the vertex group in `Density` and `Length` (the default vertex group is `Default`).
|
||||
|
||||

|
||||
|
||||
## Troubleshooting
|
||||
Some tips for troubleshooting:
|
||||
- Don't apply the 90-degree rotation on the Y axis to the Dupli Object, that rotation is just to see the object well placed in Blender
|
||||
- Make sure that the origin of the Dupli Object is centered in the base
|
||||
- Apply any scale used on the Dupli and Base Objects
|
||||
- Check the logs for any errors or warnings (in Windows: Window > Toggle System Console)
|
||||
|
||||
## Automation
|
||||
After adding the second particle system you will probably be asking for a way to automate the process of adding more foliage/debris layers to the scene.
|
||||
|
||||
That is possible with a very simple Blender script, switch to the `Scripting` layout and copy the following script:
|
||||
```
|
||||
import bpy
|
||||
|
||||
def add_debris_layer( object_name, seed=1, number=100, phase=1, random=1, size=1, size_rnd=1 ):
|
||||
|
||||
bpy.ops.object.particle_system_add()
|
||||
|
||||
index = len(bpy.context.object.particle_systems) - 1
|
||||
particle_system = bpy.context.object.particle_systems[index]
|
||||
|
||||
particle_system_settings = particle_system.settings
|
||||
|
||||
particle_system.name = object_name
|
||||
particle_system_settings.name = object_name
|
||||
|
||||
particle_system_settings.use_advanced_hair = True
|
||||
|
||||
particle_system_settings.type = "HAIR"
|
||||
particle_system_settings.hair_length = 1
|
||||
particle_system_settings.count = number
|
||||
|
||||
particle_system.seed = seed
|
||||
|
||||
particle_system_settings.use_rotations = True
|
||||
particle_system_settings.rotation_mode = "NOR"
|
||||
particle_system_settings.phase_factor = phase
|
||||
particle_system_settings.phase_factor_random = random
|
||||
|
||||
particle_system_settings.render_type = "OBJECT"
|
||||
particle_system_settings.use_rotation_dupli = True
|
||||
|
||||
particle_system_settings.dupli_object = bpy.data.objects[object_name]
|
||||
particle_system_settings.particle_size = size
|
||||
particle_system_settings.size_random = size_rnd
|
||||
|
||||
bpy.ops.object.vertex_group_add()
|
||||
|
||||
index = len(bpy.context.object.vertex_groups) - 1
|
||||
vertex_group = bpy.context.object.vertex_groups[index]
|
||||
|
||||
vertex_group.name = object_name
|
||||
|
||||
particle_system.vertex_group_density = object_name
|
||||
particle_system.vertex_group_length = object_name
|
||||
|
||||
# Example usage:
|
||||
add_debris_layer("Bush", 1)
|
||||
add_debris_layer("Grass 1", 2)
|
||||
add_debris_layer("Stone 1", 5)
|
||||
add_debris_layer("Tree 1", 10)
|
||||
```
|
||||
101
assets/blender/scripts/blender2ogre/Physics.md
Normal file
@@ -0,0 +1,101 @@
|
||||
|
||||
# Exporting for Physics Engines
|
||||
|
||||
## Index
|
||||
- [Introduction](#introduction)
|
||||
- [Creating a Convex Hull](#creating-a-convex-hull)
|
||||
- [Using the Decimate Modifier](#using-the-decimate-modifier)
|
||||
|
||||
## Introduction
|
||||
Since OGRE3D is a graphics engine, if you are interested in making a game then you are probably also using a physics engine.
|
||||
Some of the most popular physics engines are:
|
||||
- [Havok Physics](https://www.havok.com/)
|
||||
- [NVIDIA PhysX SDK](https://github.com/NVIDIAGameWorks/PhysX)
|
||||
- [Bullet Physics](https://pybullet.org/wordpress/)
|
||||
|
||||
In the case of Bullet Physics, there is even an official OGRE Component that connects OGRE objects to the Bullet Physics world ([Bullet-Physics to Ogre connection](https://ogrecave.github.io/ogre/api/13/group___bullet.html#details)).
|
||||
|
||||
One common issue when using physics engines that relates to `blender2ogre` is the creation of collision meshes or hulls.
|
||||
|
||||
For the physics engine to operate at maximum efficiency the collision shapes in the world should be [Convex Hulls](https://en.wikipedia.org/wiki/Convex_hull).
|
||||
|
||||
So when creating a level in Blender and then exporting the mesh to render into OGRE3D, the same mesh or the same arrangement of vertices would in most cases be very inefficient to use as a collision mesh.
|
||||
|
||||
## Creating a Convex Hull
|
||||
> **WARNING**: This section covers a feature only available since Blender version 3.0 (Geometry Nodes + Convex Hull)
|
||||
|
||||
In general for collision testing, it is most efficient to use the physics engines' primitive shapes, so if the asset can be put within one of these shapes it will consume fewer CPU resources (for example a barrel within a Cylinder Collision Shape).
|
||||
|
||||
Primitive shapes:
|
||||
- Sphere Shape
|
||||
- Plane Shape
|
||||
- Box Shape
|
||||
- Cylinder Shape
|
||||
- Capsule Shape
|
||||
- Cone Shape
|
||||
|
||||
Complex shapes:
|
||||
- Compound Shape (Collection of primitive shapes)
|
||||
- Convex Hull Shape (A good analogy for a convex hull is an elastic membrane or balloon under pressure which is placed around a given set of vertices)
|
||||
- Triangle Mesh Shape (A triangle mesh shape is similar to the convex hull shape, except that it is not restricted to convex geometry)
|
||||
- Heightfield Shape (Used for terrain, because it can take elevation data from an image)
|
||||
- Soft Body Shape
|
||||
|
||||
Primitive shapes don't concern us in this document, since they can be easily created in OGRE.
|
||||
For example `Ogre::Bullet::createSphereCollider( const MovableObject * mo )` would create a Sphere Shape for the provided OGRE Movable Object (like an Entity).
|
||||
|
||||
Our concern is regarding more complex objects that require a more detailed collision shape that better fits the shape of the object.
|
||||
|
||||
An example could be some rocks that the player can climb on top of.
|
||||
None of the primitive shapes approximate the random shape of a rock and if we tried the mismatch between the visual and the physical would be awkward for the player.
|
||||
|
||||
For this example, we are going to use the [Mossy Stone Rock](https://opengameart.org/content/mossy-stone-rock) from OpenGameArt.
|
||||
|
||||

|
||||
|
||||
To create a collision mesh of type Convex Hull for this object first we are going to duplicate it with `Shift-D`.
|
||||
|
||||
Rename the cloned object to "Stone_collision" (usually collision meshes are named like this to distinguish from the visual mesh).
|
||||
|
||||
With the cloned object selected go to Modifiers and add the `Geometry Nodes Modifer`.
|
||||
|
||||

|
||||
|
||||
Create a new Geometry Node and then change the Layout to `Geometry Nodes` (layouts are in the top bar in Blender)
|
||||
|
||||
Now add a `Geometry Node` of type Convex Hull (Add (Shift-A) -> Geometry -> Convex Hull) and put the node between the `Group Input` and the `Group Output`
|
||||
|
||||

|
||||
|
||||
This will give you a simplified convex version of the mesh that can be exported by blender2ogre and then used as a collision shape in Bullet or even PhysX.
|
||||
|
||||
> NOTE: Very important!
|
||||
Before exporting the mesh remember to set the shading to `Shade Smooth` (Object -> Shade Smooth), this is because `Flat Shading` makes `blender2ogre` export more vertices that would be necessary for this case.
|
||||
Also, remember to remove the material since the collision mesh won't need it.
|
||||
|
||||
## Using the Decimate Modifier
|
||||
But what if you want to use a model like this one:
|
||||
|
||||
[Stronghold](https://opengameart.org/content/stronghold) from OpenGameArt.
|
||||
|
||||
The object looks like this in Blender:
|
||||
|
||||

|
||||
|
||||
If we use a `Geometry Node` to create a Convex Hull, the result looks like this:
|
||||
|
||||

|
||||
|
||||
So, this cannot be used as a place where the player can go around going into the Stronghold and picking up loot.
|
||||
|
||||
In this case, a better approach is to duplicate the object and then add the `Decimate Modifier`.
|
||||
Use the `Collapse` option and play with the `Ratio` lowering it as much as possible until the geometry starts collapsing too much.
|
||||
Also:
|
||||
- Use the `Clean Up` menu (`Edit Mode`, Mesh -> Clean Up) to clean up the mesh to avoid as much degenerate geometry as possible.
|
||||
- Set the shading to `Shade Smooth` (Object -> Shade Smooth), this is because `Flat Shading` makes `blender2ogre` export more vertices that would be necessary for this case.
|
||||
- Remove the materials since the collision mesh won't need it.
|
||||
- This model has some ornaments that can be removed, like the window and door ledges which also reduces the complexity of the model.
|
||||
|
||||
The result is an object that looks bad but has a lower vertex count which will improve performance for the physics engine when calculating collisions.
|
||||
|
||||

|
||||
315
assets/blender/scripts/blender2ogre/README.md
Normal file
@@ -0,0 +1,315 @@
|
||||
|
||||
# blender2ogre #
|
||||
* License: [GNU LGPL](http://www.gnu.org/licenses/lgpl.html)
|
||||
* [Ogre forum thread](https://forums.ogre3d.org/viewtopic.php?f=8&t=61485)
|
||||
|
||||
**This versions requires Blender 2.8+.** For Blender 2.7x: [use the 2.7x-support branch](https://github.com/OGRECave/blender2ogre/tree/2.7x-support)
|
||||
|
||||
## Index
|
||||
- [Installing](#installing)
|
||||
- [Updating to new versions](#updating-to-new-versions)
|
||||
- [Video Tutorials](#video-tutorials)
|
||||
- [Exporting Meshes](#exporting-meshes)
|
||||
- [Materials](#materials)
|
||||
- [Blender Modifiers Support](#blender-modifiers-support)
|
||||
- [Mesh triangulation issues](#mesh-triangulation-issues)
|
||||
- [OgreNext Tips](#ogrenext-tips)
|
||||
- [Importing Meshes](#importing-meshes)
|
||||
- [Additional Features](#additional-features)
|
||||
- [Merge Objects on export](#merge-objects-on-export)
|
||||
- [External OGRE Materials](#external-ogre-materials)
|
||||
- [Console Export](#console-export)
|
||||
- [Exporting Custom Vertex Groups](#exporting-custom-vertex-groups)
|
||||
- [Exporting Custom Normals](#exporting-custom-normals)
|
||||
- [Exporting Skeletal Animations](#exporting-skeletal-animations)
|
||||
- [Exporting Particle Systems](#exporting-particle-systems)
|
||||
- [Exporting Shape (or Pose) Animations](#exporting-shape-animations)
|
||||
- [Exporting Node Animations](#exporting-node-animations)
|
||||
- [Exporting for Physics](#exporting-for-physics)
|
||||
- [Exporting Vertex Colors](#exporting-vertex-colors)
|
||||
- [Exporting SkyBoxes](#exporting-skyboxes)
|
||||
- [Level of Detail (LOD)](#level-of-detail-lod)
|
||||
- [Mesh Previewer](#mesh-previewer)
|
||||
- [About](#about)
|
||||
- [Authors](#authors)
|
||||
|
||||
## Installing
|
||||
1. Copy the [io_ogre](io_ogre) folder into the [$BLENDER_DIR](https://docs.blender.org/manual/en/latest/advanced/blender_directory_layout.html)`/scripts/addons` folder.
|
||||
2. Enable the addon in Blender: `Edit menu > Preferences > Add-ons`. Search for `'ogre'` and click the box up the top left.
|
||||
3. Configure the plugin before the first run.
|
||||
- Set the correct path to `OGRETOOLS_XML_CONVERTER`
|
||||
- for Ogre (v1): path should point to `OgreXMLConverter.exe`. This can be found in the [Ogre SDK](https://www.ogre3d.org/download/sdk/sdk-ogre)
|
||||
- for OgreNext (v2): path should point to `OgreMeshTool.exe`. This can be found in the [OgreNext SDK](https://www.ogre3d.org/download/sdk/sdk-ogre-next)
|
||||
- *OPTIONAL* Set `MESH_PREVIEWER` to a path pointed to `ogre-meshviewer.bat`. This can be found in [OGRECave/ogre-meshviewer](https://github.com/OGRECave/ogre-meshviewer/releases)
|
||||
- Make sure that `USER_MATERIALS` isn't set to a directory like "C:\\\". The addon scans this path recursively and will crash when it hits a path it doesn't have permissions for.
|
||||
|
||||
> **NOTE**: Installing Blender using Ubuntu Snap package or Fedora Flatpak will lead to the following error: `cp: cannot create directory '/snap/blender/3132/3.4/scripts/addons/io_ogre': Read-only file system
|
||||
` (see: [Installing on Ubuntu 20.04 and Blender 3.4.1. #169](https://github.com/OGRECave/blender2ogre/issues/169))
|
||||
|
||||
There are two possible solutions:
|
||||
- After downloading the `blender2ogre` repo, uncompress it somwhere and then compress the [io_ogre](io_ogre) folder as a .zip file. Go to `Edit` -> `Preferences (CTRL-ALT-U)` -> `Add-ons` -> `Install...` and then select the file `io_ogre.zip`
|
||||
- Copy the [io_ogre](io_ogre) folder into the folder: `~/.config/blender/3.4/scripts/addons/` (where 3.4 is the Blender version)
|
||||
|
||||
|
||||
## Updating to new versions ##
|
||||
If you are upgrading from a previous version of blender2ogre and having problems, you may want to delete your old .json config file from
|
||||
[$BLENDER_DIR](https://docs.blender.org/manual/en/latest/advanced/blender_directory_layout.html)`/config/scripts/io_ogre.json` and restart blender.
|
||||
|
||||
## Video Tutorials
|
||||
* [General Usage](http://www.youtube.com/watch?feature=player_embedded&v=3EpwEsB0_kk)
|
||||
* [Animations](http://www.youtube.com/watch?feature=player_embedded&v=5oVM0Lmeb68)
|
||||
* [Meshmoon: Video and text instructions on how to install and use blender2ogre addon](http://doc.meshmoon.com/index.html?page=from-blender-to-meshmoon-part-1)
|
||||
|
||||
## Exporting Meshes
|
||||
To export a blender model: `File Menu > Export > Ogre3D (.scene & .mesh)`.
|
||||
|
||||
If the menu button is greyed out (or you get this error: `RuntimeError: Operator bpy.ops.ogre.export.poll() failed, context is incorrect`), then make sure there is an active object selection in the blender Node tree (Scene collections) first.
|
||||
The active object selection is when there is an object with a yellow outline (in contrast to the orange outline of the passive selected objects)
|
||||
|
||||
- If you have `OGRETOOLS_XML_CONVERTER` set to "OgreXMLConverter.exe" path, then the export dialogue will display options relevant to the Ogre (v1) mesh format.
|
||||
- If you have `OGRETOOLS_XML_CONVERTER` set to "OgreMeshTool.exe" path, then the export dialogue will display options relevant to the OgreNext (v2) mesh format.
|
||||
|
||||
Check out all the exporter and importer options in the [Options Document](Options.md)
|
||||
|
||||
### Materials
|
||||
Materials will be exported as OGRE 1.x material files or as OGRE Next material.json files as required.
|
||||
|
||||
#### OGRE 1.x Materials
|
||||
Materials are exported as RTSS OGRE 1.x materials (unless "Fixed Function Parameters" is selected).
|
||||
The following textures are exported: Base Color, Metallic and Roughness, Normal Map and Emission. Baked Ambient Occlusion is not supported for the moment.
|
||||
|
||||
Your material will be best exported if you follow the GLTF2 guidelines: [glTF 2.0 - Exported Materials](https://docs.blender.org/manual/en/2.80/addons/io_scene_gltf2.html#exported-materials).
|
||||
Except for the Emission texture, where the Emission input of the Principled BSDF node is used as the Emission texture.
|
||||
|
||||
A good example of how the material should be setup for best results is the "Damaged Helmet" model found here: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/DamagedHelmet
|
||||
|
||||
#### OGRE Next JSON Materials
|
||||
The current OGRE Next JSON exporter only explicitly supports a metalness workflow, however it will attempt to export materials not following that workflow regardless and *may* produce passable results.
|
||||
|
||||
For materials using metalness it expects a single image with the blue channel containing the metalness map and the green channel containing the roughness map fed into the Principled BSDF via a Separate RGB node. Not following this convention will print a warning to the console and the exported materials will likely not appear correctly when rendered.
|
||||
|
||||
There are numerous features in the Ogre Next JSON format that are not directly supported, see the [Materials JSON](MaterialsJSON.md) notes for details.
|
||||
|
||||
### Blender Modifiers Support
|
||||
Blender has some very useful modifiers, and most of them are supported by `blender2ogre` but not all of them.
|
||||
Check out the [Blender Modifiers Support Page](Modifiers.md) to check out the list and also some recommendations about them.
|
||||
|
||||
### Mesh triangulation issues
|
||||

|
||||
|
||||
If you are seeing some issues with the mesh triangulation (like visible triangles in your shading), check the [Mesh Triangulation README](MeshTriangulation.md) to learn more about the subject and how to work around it.
|
||||
|
||||
### OgreNext tips
|
||||
If you do want to export in the OgreNext (v2.) format, make sure in the `Export dialogue > General Settings > Mesh Export Version` is set to V2. The following parameters are a good starting point to get a model exported to an Ogre mesh:
|
||||
* General
|
||||
- Mesh export version: v2
|
||||
* Materials
|
||||
- Export materials: ticked
|
||||
* Armature
|
||||
- Armature animation: ticked
|
||||
* Mesh
|
||||
- Export mesh: ticked
|
||||
- Edge lists: un-ticked
|
||||
- If your model's materials contain normal mapping:
|
||||
- Tangents: "generate with parity"
|
||||
- Else Tangents: "none"
|
||||
- Optimise Vertex buffers for shaders: ticked
|
||||
- Vertex buffer options: **puqs**
|
||||
|
||||
You can check the arguments passed to `OgreMeshTool.exe` in the Blender console. (`Window Menu > Toggle System Console`)
|
||||
|
||||
Blender will export the material format in an Ogre (V1) format. This is not compatible with OgreNext (V2.*). You should manually convert them to a material.json file. See the [Ogre Wiki: HLMS Materials](https://wiki.ogre3d.org/HLMS+Materials) for more information.
|
||||
|
||||
## Importing Meshes
|
||||
As of `blender2ogre` version *0.8.2*, the Kenshi Importer has been integrated into `blender2ogre` with the following features:
|
||||
- Import mesh from `.xml` as well as `.mesh` files
|
||||
- Import whole `.scene` files (as of version *0.8.5*) with all its objects
|
||||
- Option to be able to merge imported submeshes or keep them separate
|
||||
- Parsing/Conversion of materials into Blender (just create a Principled BSDF material and add a texture with the proper UV mapping)
|
||||
- Importing of Poses
|
||||
- Importing of Skeletons works for the most part, but Ogre skeletons conventions are not like Blenders (see: [How to get bone's vector and bone's length?](https://forums.ogre3d.org/viewtopic.php?t=49689))
|
||||
- Importing of Animations work, but depends on the skeleton which sometimes doesn't get correctly imported
|
||||
- As of Blender 4.1+, it is now possible to drag and drop files of types: `.xml`, `.mesh` or `.scene` and import them automatically. (https://docs.blender.org/api/4.1/bpy.types.FileHandler.html)
|
||||
|
||||
> **NOTE:** Orientation of the imported mesh is assumed to be `xz-y` (Ogre default), the `blender2ogre` Axis Swapping option does not work for the importing process.
|
||||
|
||||
## Additional Features
|
||||
|
||||
### Merge Objects on export
|
||||
You might have hundreds of objects, which you want to keep separate but have them in one `.mesh` on export.
|
||||
For this create a new collection (M) named `merge.<yourname>`. The output will be a single `<yourname>.mesh` file. Alternatively link the collection.
|
||||
|
||||
> **NOTE:** The origin of the resulting merged object will be that of the *last* object you added to the collection (although when reloading the blend file, this order will be lost).
|
||||
To have control over the precise location of where the merged objects' origin will be, use the `dupli_offset` property of the collection.
|
||||
Setting any value other than the default `(0, 0, 0)` will result in a mesh with the origin set to that value. For example:
|
||||
|
||||

|
||||
|
||||
### Instancing and DotScene Plugin
|
||||
As of OGRE 1.13 a new feature has been added to the DotScene Plugin where it now accepts the static / instanced keywords for entities.
|
||||
(for more information read the [DotScene Plugin README](https://github.com/OGRECave/ogre/blob/master/PlugIns/DotScene/README.md)).
|
||||
|
||||
To use this feature create a new collection (M) names as `static.<Group Name>` or `instanced.<Instance Manager Name>` and blender2ogre will automatically add the corresponding attribute to the exported entities in the Scene.
|
||||
This feature goes hand in hand with [Exporting Particle Systems](#exporting-particle-systems) to create vegetation, debris and other static objects in your scene.
|
||||
|
||||
### External OGRE Materials
|
||||
You might already have some materials in OGRE that you do not want to export.
|
||||
Prefix them with `extern.<yourname>` and the sub-entity will have the material name set, but the material is not exported.
|
||||
The following material 'vertexcolor' can be defined in your OGRE project:
|
||||
|
||||

|
||||
|
||||
### Console Export
|
||||
You might have several blender files in your project you want to export to Ogre. Do this by hand? NO! You can do better!
|
||||
Here is how you can export a scene with blender2ogre.
|
||||
|
||||
```sh
|
||||
blender test.blend -b --python-expr "import bpy;bpy.ops.ogre.export(filepath='test.scene')"
|
||||
```
|
||||
|
||||
### Exporting Custom Vertex Groups
|
||||
As shown in the picture below, you can now export SubEntities that contain a user-defined amount of faces.
|
||||
|
||||

|
||||
|
||||
You simply call your vertex group with the
|
||||
prefix `ogre.vertex.group.<yourname>` and access it in Ogre similar to the following:
|
||||
|
||||
```cpp
|
||||
void example(const Ogre::Entity * entity)
|
||||
{
|
||||
auto collision = entity->getSubEntity("collision");
|
||||
auto mesh = collision->getSubMesh();
|
||||
VertexData * data = nullptr;
|
||||
if (!mesh->useSharedVertices) {
|
||||
data = mesh->vertexData;
|
||||
} else {
|
||||
data = mesh->parent->sharedVertexData;
|
||||
}
|
||||
auto pos_cursor = data->vertexDeclaration->findElementBySemantic(Ogre::VES_POSITION);
|
||||
auto vbuffer = data->vertexBufferBinding->getBuffer(pos_cursor->getSource());
|
||||
auto ibuffer = mesh->indexData->indexBuffer;
|
||||
|
||||
uint16_t * indices = static_cast<uint16_t*>(ibuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
float * vertices = static_cast<float*>(vbuffer->lock(Ogre::HardwareBuffer::HBL_READ_ONLY));
|
||||
float * v;
|
||||
int count = mesh->indexData->indexCount;
|
||||
int stride = vbuffer->getVertexSize() / 4;
|
||||
for (int i = 0; i < count; i+=3) {
|
||||
uint16_t a = indices[i], b = indices[i+1], c = indices[i+2];
|
||||
pos_cursor->baseVertexPointerToElement(vertices + a * stride, &v);
|
||||
Vector3 va(v);
|
||||
pos_cursor->baseVertexPointerToElement(vertices + b * stride, &v);
|
||||
Vector3 vb(v);
|
||||
pos_cursor->baseVertexPointerToElement(vertices + c * stride, &v);
|
||||
Vector3 vc(v);
|
||||
// do something with your triangle here
|
||||
}
|
||||
ibuffer->unlock();
|
||||
vbuffer->unlock();
|
||||
}
|
||||
```
|
||||
The vertex group will get the material name 'none' so you might want to add the following script:
|
||||
|
||||
```
|
||||
material none {
|
||||
technique {
|
||||
pass {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exporting Custom Normals
|
||||

|
||||
|
||||
Custom Split Normals is a way to tweak the mesh shading by pointing normals towards directions other than the default, auto-computed ones.
|
||||
It is mostly used in game development, where it helps counterbalance some issues generated by low-poly objects
|
||||
(the most common examples are low-poly trees, bushes, grass, etc. and the ‘rounded’ corners).
|
||||
Check out the [Custom Normals README](CustomSplitNormals.md) to learn more about Custom Normals in Blender.
|
||||
|
||||
### Exporting Skeletal Animations
|
||||

|
||||
|
||||
Skeletal Animation refers to the technique of using bones to deform a mesh as if the mesh were the skin.
|
||||
This kind of animation is commonly used to animate characters in video games.
|
||||
Check out the [Skeletal Animations README](SkeletalAnimation.md) to see how to create and export an animated mesh.
|
||||
|
||||
### Exporting Particle Systems
|
||||

|
||||
|
||||
A common technique for laying out random objects on a scene in Blender is to use the Particle System.
|
||||
Check out the [Particle System README](ParticleSystem.md) to see how to create and export a scene where the trees, foliage and rocks are distributed randomly using a particle system.
|
||||
|
||||
### Exporting Shape Animations
|
||||

|
||||
|
||||
Shape (or Pose) Animations allow animating different poses, a technique commonly used to do face animations.
|
||||
Check out the [Shape Animations](ShapeAnimations.md) tutorial to see how to create some poses and animate them.
|
||||
Then you can use `blender2ogre` to export the poses and animations into a `.mesh` file.
|
||||
|
||||
### Exporting Node Animations
|
||||
Node Animations are a way to have scripted node animations in your Ogre application.
|
||||
Check out the [Node Animations](NodeAnimations.md) tutorial to see how to create some animations for a couple of different scenarios.
|
||||
|
||||
### Exporting for Physics
|
||||
Check out the [Exporting for Physics](Physics.md) tutorial to see some techniques and optimizations when exporting collision meshes for Physics Engines
|
||||
|
||||
### Exporting Vertex Colors
|
||||

|
||||
|
||||
`blender2ogre` can import and export Vertex Colors, a way to assign colors to your mesh by vertex. It is also possible to asign alpha values to each vertex for transparency or other uses.
|
||||
Check out the [Vertex Colors](VertexColors.md) tutorial to see how to create the Vertex Colors in Blender and how to export them.
|
||||
|
||||
### Exporting SkyBoxes
|
||||

|
||||
|
||||
`blender2ogre` can generate SkyBoxes from HDRi Maps and export them in a format that can be used in OGRE, that is a Cube Map.
|
||||
Check out the [SkyBoxes](SkyBoxes.md) tutorial to see how to create import the Environment Map in Blender and how to export it.
|
||||
|
||||
|
||||
### Level of Detail (LOD)
|
||||
Level of Detail or LOD is an optimization technique supported by OGRE, where meshes that are far away from the camera are replaced by meshes with lower vertex count.
|
||||
Because you can get away with less detailed models when they are in the distance this optimizacion technique is very common, especially in games.
|
||||
With `blender2ogre` there are three ways to generate LOD levels for a mesh:
|
||||
* `OgreMesh Tools`: This method uses the tool `OgreMeshUpgrader` to automatically generate the LOD levels for the mesh by removing edges. This allows only changing the index buffer and re-use the vertex-buffer (storage efficient)
|
||||
* `Blender`: This method uses Blenders "Decimate" Modifier to automatically generate the LOD Leves by collapsing vertices. This can result in a visually better LOD, but needs different vertex-buffers per LOD
|
||||
* `Manual`: This metod requires that the user manually creates the additional LOD levels for the mesh. The meshes should be called [base mesh]_LOD_1, [base mesh]_LOD_2, etc. This method gives better control over the resulting LODs.
|
||||
|
||||
For the `Manual` method it is currently not possible to manually set the distances for each LOD, but it is possible to have different materials for each LOD level.
|
||||
|
||||
|
||||
### Mesh Previewer
|
||||
If `MESH_PREVIEWER` is set, a button will appear allowing you to preview your mesh in Ogre3D. If the button isn't there, the path is invalid. This only works for Ogre (V1) meshes.
|
||||
The button is located here:
|
||||
|
||||

|
||||
|
||||
|
||||
## About
|
||||
[The original version of this](https://code.google.com/archive/p/blender2ogre/) was a *single* monolithic Python file.
|
||||
This is not maintainable and contains a tremendous amount of bugs. There was the need to export Blender model to OGRE from
|
||||
the console, thus I rewrote the whole script and split it into several files.
|
||||
It has been well-tested on Linux 64-bit and should work with others.
|
||||
|
||||
## Authors
|
||||
This Blender addon was made possible by the following list of people.
|
||||
Anyone can contribute to the project by sending bug reports and feature requests [here](https://github.com/OGRECave/blender2ogre/issues).
|
||||
Naturally, the most welcome contribution is actual code via [pull requests](https://github.com/OGRECave/blender2ogre/pulls).
|
||||
If you are planning to implement something "big", it's a good practice to discuss it in the issue tracker first with other authors.
|
||||
So that there is no overlap with other developers or the overall roadmap.
|
||||
|
||||
* [Git Contributors](https://github.com/OGRECave/blender2ogre/graphs/contributors)
|
||||
* [Brett](http://pyppet.blogspot.fi/)
|
||||
* S. Rombauts
|
||||
* F00bar
|
||||
* Waruck
|
||||
* [Mind Calamity](https://bitbucket.org/MindCalamity)
|
||||
* Mr.Magne
|
||||
* [Jonne Nauha](https://bitbucket.org/jonnenauha) aka Pforce
|
||||
* vax456
|
||||
* Sybren Stüvel
|
||||
|
||||
Additionally, the following companies have supported/sponsored the development efforts.
|
||||
* [Adminotech Ltd.](http://www.meshmoon.com/)
|
||||
121
assets/blender/scripts/blender2ogre/ShapeAnimations.md
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
# Shape Animations
|
||||
|
||||
## Index
|
||||
- [Introduction](#introduction)
|
||||
- [Set up](#set-up)
|
||||
- [Baking complex animations into Shape Animations](#baking-complex-animations-into-shape-animations)
|
||||
- [Using Shape Animations in Ogre](#using-shape-animations-in-ogre)
|
||||
|
||||
## Introduction
|
||||
A common technique for creating face animations is to use Poses or Shapes for the different phonemes that the character should go through when talking.
|
||||
|
||||
In this tutorial, we are going to explore how to create a couple of poses and animate them.
|
||||
|
||||
Terminology:
|
||||
- *Shape Keys*: a certain pose defined in Blender. This pose can be blended with other poses to different degrees to achieve new poses
|
||||
|
||||
## Set up
|
||||
To make this tutorial simple, the Animation is going to be applied only to the default cube.
|
||||
|
||||
To see a better representation of face animations, take a look at [this](examples/shape-animation.blend) example.
|
||||
|
||||
Create a new scene in Blender and split it into two, in the second view select `Dope Sheet` and `Shape Key Editor`.
|
||||

|
||||
|
||||
Select the default cube and go to the `Vertex` or `Data` tab. Under `Shape Keys` press the `+` button three times to create three Shape Keys.
|
||||

|
||||
|
||||
The Basis is the base shape and the other shape keys are going to be the poses that we apply over the base.
|
||||
|
||||
Select `Key 1` and go into `Edit Mode` (press tab).
|
||||
|
||||
Once you are in `Edit Mode` press `s` (scale), 2 and `Enter` to scale the cube two times.
|
||||
|
||||
Then select `Key 2` and press again `s`, write 0.5 and `Enter` to scale the cube to half size.
|
||||
|
||||
Now you have three shapes to animate.
|
||||
|
||||
Go into `Object Mode` by pressing tab and at the bottom you should see that the current frame is 1.
|
||||

|
||||
|
||||
Select `Key 1` and in `value` set it all the way to 1.
|
||||
|
||||
Hover the mouse over `value` and press `i` to insert a keyframe at frame 1.
|
||||
|
||||
Then, select `Key 2` and leave `value` as it is, Hover the mouse over `value` and press `i` to insert a keyframe at frame 1 for `Key 2`.
|
||||
|
||||
You should see in the `Dope Sheet` view that now there is a new "Key Action", press `F` next to it to save the action by associating it with a fake user.
|
||||
|
||||
Now, set the current frame to 60 and repeat the above process but setting `value` to 0 for `Key 1` and 1 for `Key 2`, remember to insert the keyframes (the `value`s should turn yellow).
|
||||
|
||||
Set the `End` frame to 60 and press play to see the cube changing size. Of course, this is a simple animation but with some imagination, something much more complex can be achieved like face animations.
|
||||
|
||||
Last but not least for the `blender2ogre` add-on to properly export the animation it is necessary to turn it into an NLA Track, and select the `Push Down` button next to the action name.
|
||||

|
||||
|
||||
You can now go into the `NLA Editor` view and change the name of the NLA Track that name is the one that is going to be exported.
|
||||
|
||||
## Baking complex animations into Shape Animations
|
||||
[How to Bake Modifier Animation in Blender / 1. Wave Modifier Animation to Shape Keys!](https://www.youtube.com/watch?v=KMIkOhTSP1U)
|
||||
https://docs.blender.org/manual/en/latest/addons/import_export/shape_mdd.html
|
||||
|
||||
Blender can perform some complex vertex animations (`Wave Modifier` being an example).
|
||||
However, it is not possible to just export these animations into OGRE directly.
|
||||
But there is a trick to baking these animations into Shape Key Animations and then it is possible to export into OGRE.
|
||||
The trick consists of exporting the animation using the `NewTek MDD` format and then importing it, the resulting mesh will have the vertex animations baked as a Shape Key Animation
|
||||
|
||||
> NOTE: Care must be taken if the animation has too many frames since there will be one Shape Key for every frame and that makes the exported mesh heavier.
|
||||
|
||||
The steps are the following (for example using `Wave Modifier`):
|
||||
1) Add a plane mesh (Shift-A -> Mesh -> Plane), then enter `Edit Mode` (Tab) and subdivide the mesh (Ctrl-E -> Subdivide) 5 times so the `Wave Modifier` has some geometry to work with
|
||||
2) Set the object shading to smooth (Object -> Shade Smooth)
|
||||
3) Add the `Wave Modifier` to the "Plane" Object and rename the "Plane" to "Wave"
|
||||
4) Set the starting and ending frames of the animation (lower right corner)
|
||||
5) Enable the `NewTek MDD` Add-On (go to Edit -> Preferences -> Add-ons -> `NewTek MDD` and enable the Add-on)
|
||||
6a) Make sure the Plane/Wave object is selected
|
||||
6b) Export the animation to an .mdd file: File -> Export -> Lightwave Point Cache (.mdd) and set a proper filename like wave.mdd
|
||||
7) Duplicate the Plane object (Shift-D) and set a name like Wave2
|
||||
8) On the Duplicate Plane object, remove the Wave modifier
|
||||
9a) Make sure the duplicate object "Wave2" is selected
|
||||
9b) Now import the recently exported .mdd file: File -> Import -> Lightwave Point Cache (.mdd)
|
||||
Now the duplicate object "Wave2" has a number of Shape Keys, each for every frame that was exported in step 6b)
|
||||
Besides a new action `KeyAction` is created, which you can see in the `Shape Key Editor` of the `Dope Sheet` (Shift-F12)
|
||||
10) Now to get blender2ogre to export the animation we need to create a NLA track, go to the `Animation` and in the upper left corner change the view to `Nonlinear Animation`
|
||||
11) Perform a push-down of the animation toward an NLA Track
|
||||
12) Set the name of the NLA Track, which will be the name of the Shape/Pose Animation in OGRE
|
||||
13) Now use `blender2ogre` to export the animation, make sure the option `SHAPE_ANIMATIONS` is set to `True`
|
||||
|
||||
|
||||
## Using Shape Animations in Ogre
|
||||
Create an Entity and attach it to a SceneNode
|
||||
```
|
||||
Ogre::Entity* cube = mSceneMgr->createEntity("Cube", "Cube.mesh");
|
||||
Ogre::SceneNode* cubeNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("Cube");
|
||||
cubeNode->attachObject(cube);
|
||||
```
|
||||
|
||||
Get the AnimationState, enable it and set the starting time position
|
||||
```
|
||||
auto animationState = cube->getAnimationState("KeyAction");
|
||||
animationState->setEnabled(true);
|
||||
animationState->setTimePosition(0);
|
||||
```
|
||||
|
||||
Then you need to `addTime()` to the *AnimationState*, we will use a controller for that.
|
||||
```
|
||||
auto& controllerMgr = Ogre::ControllerManager::getSingleton();
|
||||
|
||||
// Create a controller to pass the frame time to the Animation State, otherwise the animation won't play
|
||||
// (this is a better method than using animationState->addTime() in your main loop)
|
||||
controllerMgr.createFrameTimePassthroughController(Ogre::AnimationStateControllerValue::create(animationState, true));
|
||||
```
|
||||
|
||||
For more information, please take a look at section [Vertex-Animation](https://ogrecave.github.io/ogre/api/latest/_animation.html#Vertex-Animation) in the manual.
|
||||
|
||||
And also consult the Ogre API manual:
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_manager.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_animation_state.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_node.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_controller_manager.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_controller.html
|
||||
219
assets/blender/scripts/blender2ogre/SkeletalAnimation.md
Normal file
@@ -0,0 +1,219 @@
|
||||
|
||||
# Exporting Skeletal Animations
|
||||
|
||||
## Index
|
||||
- [Introduction](#introduction)
|
||||
- [Creating a basic rig](#creating-a-basic-rig)
|
||||
- [How does the exporter work?](#how-does-the-exporter-work-)
|
||||
- [Smooth Corrective Modifier](#smooth-corrective-modifier)
|
||||
- [Exporter Options](#exporter-options)
|
||||
- [Using Skeletal Animations in Ogre](#using-skeletal-animations-in-ogre)
|
||||
- [Tips/Troubleshooting](#tips-troubleshooting)
|
||||
- [Human Top+Base Animations](#human-top-base-animations)
|
||||
- [Character clothing](#character-clothing)
|
||||
- [Shared Skeleton](#shared-skeleton)
|
||||
- [Base Mesh/Skin clipping](#base-mesh-skin-clipping)
|
||||
- [Random Tips from the forum](#random-tips-from-the-forum)
|
||||
- [Automation](#automation)
|
||||
|
||||
## Introduction
|
||||
Skeletal Animation refers to the technique of using bones to deform a mesh as if the mesh were the skin.
|
||||
The bones deform different parts of the mesh based on vertex weights that specify how much any bone should influence a certain part of the mesh.
|
||||
This kind of animation is commonly used to animate characters in video games, but it's not limited to that.
|
||||
|
||||
Refer to the Blender Manual to know more about rigging: https://docs.blender.org/manual/en/2.79/rigging/index.html
|
||||
|
||||
## Creating a basic rig
|
||||
To simplify the tutorial we will use the default cube just to show the basic concepts, for more complex tutorials check YouTube, for example:
|
||||
- [Blender 2.8 Tutorial : Rig ANY Character for Animation in 10 Minutes!](https://www.youtube.com/watch?v=SBYb1YmaOMY)
|
||||
- [Blender 2.82 Rigging Tutorial (In 2 Minutes!!!)](https://www.youtube.com/watch?v=PFaqjwpGxOc)
|
||||
|
||||
Start a new Blender scene with `Ctrl-N`.
|
||||
|
||||
Press `Shift-A -> Armature -> Single Bone` to add an Armature consisting of a single bone.
|
||||
|
||||
Select the Cube, then select the Armature (in that order) and press `Ctrl-P -> Armature Deform (With Automatic Weights)`.
|
||||
|
||||
Now the default cube is a child of the Armature and Blender has automatically created a Vertex Group named *Bone* on the Cube.
|
||||
|
||||
Select the Cube and go to the `Data` tab (the one that looks like an inverted triangle), there you will see a Vertex Group named *Bone*.
|
||||
|
||||
If you change the interaction mode to `Weight Paint` you will see that the cube suddenly becomes red, that color represents the strength of the weights assigned to the bone *Bone* where red is the strongest weight and blue is the weakest.
|
||||
|
||||
Split the Blender viewport to open a second view, bring up the Dopesheet editor and select the Action Editor context.
|
||||
Click "New" to add a new action and set a name for it, also don't forget to press the `F` button next to it (in Blender 2.7x, it is a shield in Blender 2.8+).
|
||||
The purpose of that button is to avoid getting into a situation where the Action you create to animate your object has 0 users and the next time you open your .blend file the animation is gone.
|
||||
One way for this to happen is if you press the `X` button then the action is no longer associated with the Armature and you will see a 0 next to it (it has 0 users) so Blender does not save it to disk.
|
||||
|
||||
Select the Armature and then set the mode to `Pose Mode`, this mode allows you to animate the bones.
|
||||
Select the bone and now each time you press `I` a Keyframe is inserted and the proper keys for that bone are set.
|
||||
Blender will ask each time which keys you want to insert, to make this process less tedious there is a box underneath the timeline where you can set `LocRot` for Blender to automatically insert location and rotation keys for each Keyframe you insert.
|
||||
|
||||
So, make sure that the current frame is `1` and insert a Keyframe. You will see that in the dopesheet now there is a Keyframe in the action.
|
||||
|
||||
Another interesting tidbit about actions is that they are composed of F-Curves and there is an F-Curve for each attribute of the bone being animated (location.x, location.y,... rotation.x, etc.).
|
||||
And these F-Curves reference a vertex group by name (*Bone* in our case), an interesting thing is that if you have another Armature with bones with the same names and select the action in the dope sheet with that Armature selected then your animation will apply to that Armature as well. Of course, if the bones in the second Armature have the same names but different positions and orientations then the animation will look all wrong.
|
||||
Transferring one set of animations from one Rig (a particular bone layout) to another is called Animation Retargeting, this is commonly used to transfer motion capture animations.
|
||||
There is an official Blender plugin for Animation Retargeting but it is no longer being maintained, it is best to look up the subject in Google or YouTube.
|
||||
|
||||
OK, now go to frame `60` and move the bone to a different position/rotation of your choosing, the Cube should move with the bone since the weights have maximum influence. Insert another Keyframe with `I`.
|
||||
|
||||

|
||||
|
||||
Now there are two keyframes and you have a basic animation, set the `End:` frame to 60 and play the animation with `Alt-A` the cube should move and/or rotate from its original position to the one you set.
|
||||
|
||||
After finishing with the animation, press `Push Down` next to the action to add the action to the NLA Stack. This will indicate to the exporter that you want to export this action.
|
||||
|
||||
Change the Dopesheet editor to the NLA editor view and check that the action has an NLA Track, is advisable to change the NLA Track name since that will be the name of the animation in Ogre.
|
||||
|
||||
You can also play with an already made model to see how the process goes: [Stacy-rigged](https://www.turbosquid.com/FullPreview/Index.cfm/ID/535459).
|
||||
It is advisable to try an export your model at every step of the process of making it more complex, to avoid getting into a situation where you put a lot of work into it and there are problems with the export.
|
||||
|
||||
## How does the exporter work?
|
||||
To accommodate for complex animations with Inverse Kinematics, Drivers and modified F-Curves the exporter cannot simply export the Keyframes to Ogre because all the influences of these modifiers would be lost.
|
||||
So what the exporter does is go frame by frame and have Blender calculate the bone transforms and export that information to the skeleton.xml file.
|
||||
This means that your animation in Ogre has a keyframe for every single frame from the start of the animation to the end.
|
||||
As a result of this setting `IM_SPLINE` for frame interpolation in Ogre would make no difference and might even slow down the skeletal animation.
|
||||
|
||||
If you only require key frames exported, then make sure the "Only Keyframes" option is ticked in the exporter properties.
|
||||
|
||||
## Smooth Corrective Modifier
|
||||
https://docs.blender.org/manual/en/latest/modeling/modifiers/deform/corrective_smooth.html
|
||||
The Smooth Corrective modifier is used to reduce highly distorted areas of a mesh by smoothing the deformations.
|
||||
This is typically useful after an Armature modifier, where distortion around joints may be hard to avoid, even with careful weight painting.
|
||||
|
||||
## Exporter Options
|
||||
- *EX_ARMATURE_ANIMATION* (Armature Animation) : Export armature animations (updates the .skeleton file), enable this option to export the armature animations.
|
||||
- *EX_SHARED_ARMATURE* (Shared Armature) : Export a single .skeleton file for objects that have the same Armature parent (useful for: `shareSkeletonInstanceWith()`). NOTE: The name of the .skeleton file will be that of the Armature",
|
||||
- *EX_ONLY_KEYFRAMES* (Only Keyframes) : Exports only the keyframes. Influences that Inverse Kinematics, Drivers and modified F-Curves have on the animation will be lost.
|
||||
- *EX_ONLY_DEFORMABLE_BONES* (Only Deformable Bones) : Only exports bones that are deformable. Useful for hiding IK-Bones used in Blender. NOTE: Any bone with deformable children/descendants will be output as well
|
||||
- *EX_ONLY_KEYFRAMED_BONES* (Only Keyframed Bones) : Only exports bones that have been keyframed for a given animation. Useful to limit the set of bones on a per-animation basis
|
||||
- *EX_OGRE_INHERIT_SCALE* (OGRE Inherit Scale) : Whether the OGRE bones have the 'inherit scale' flag on. If the animation has scale in it, the exported animation needs to be adjusted to account for the state of the inherit-scale flag in OGRE.
|
||||
- *EX_TRIM_BONE_WEIGHTS* (Trim Weights) : Ignore bone weights below this value (Ogre supports 4 bones per vertex)
|
||||
|
||||
## Using Skeletal Animations in Ogre
|
||||
Create an Entity and attach it to a SceneNode
|
||||
```
|
||||
Ogre::Entity* cube = mSceneMgr->createEntity("Cube", "Cube.mesh");
|
||||
Ogre::SceneNode* cubeNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("Cube");
|
||||
cubeNode->attachObject(cube);
|
||||
```
|
||||
|
||||
Get the AnimationState, enable it and set the starting time position
|
||||
```
|
||||
auto animationState = cube->getAnimationState("ArmatureAction");
|
||||
animationState->setEnabled(true);
|
||||
animationState->setTimePosition(0);
|
||||
```
|
||||
|
||||
Then you need to `addTime()` to the *AnimationState*, we will use a controller for that.
|
||||
```
|
||||
auto& controllerMgr = Ogre::ControllerManager::getSingleton();
|
||||
|
||||
// Create a controller to pass the frame time to the Animation State, otherwise the animation won't play
|
||||
// (this is a better method than using animationState->addTime() in your main loop)
|
||||
controllerMgr.createFrameTimePassthroughController(Ogre::AnimationStateControllerValue::create(animationState, true));
|
||||
```
|
||||
|
||||
If you are going to blend between animations
|
||||
```
|
||||
SkeletonInstance* skel = cube->getSkeleton();
|
||||
skel->setBlendMode( ANIMBLEND_CUMULATIVE );
|
||||
```
|
||||
|
||||
For more information, please take a look at section [Vertex-Animation](https://ogrecave.github.io/ogre/api/latest/_animation.html#Vertex-Animation) in the manual.
|
||||
|
||||
And also consult the Ogre API manual:
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_animation_state.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_controller.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_controller_manager.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_entity.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_manager.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_node.html
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_skeleton.html
|
||||
|
||||
## Tips/Troubleshooting
|
||||
Some useful tips to avoid problems or diagnose issues:
|
||||
- Before parenting a mesh to an armature remember to apply location, rotation and scale to avoid problems.
|
||||
- Remember to add your actions as NLA strips, otherwise they won't be recognized by the exporter.
|
||||
- If you're using IK bones, you need to keyframe the bones it is affecting as well otherwise the animation won't work in Ogre.
|
||||
|
||||
## Human Top+Base Animations
|
||||
It is common for humanoid models to separate animations into top and bottom animations,
|
||||
that is for example to have a running animation separated into the legs animation (Base) and the torso + head animation (Top).
|
||||
The objective is that if there is another animation (shooting an arrow, for example which involves only the Top bones)
|
||||
then it is possible to combine the animations: `RunningBase` and `ShootingTop` to have the character running and shooting arrows at the same time.
|
||||
|
||||
One good example of this comes with the Ogre SDK: Sinbad.zip. The Sinbad model has a `RunBase` and `RunTop` running animations.
|
||||
Both animations have to be performed at the same time for the Sinbad character model to run using both arms and legs.
|
||||
|
||||
This requires on the Blender side creating the `RunningBase` and `RunningTop` animations, and for each one to *only* have keyframes the bones that correspond to the Top or Base of the model.
|
||||
|
||||
## Character clothing
|
||||
|
||||
### Shared Skeleton
|
||||
If you want your game characters to have clothing a common thing to do is have a base mesh with the Player/Non-Player skin and then sharing the same Armature (or Skeleton) other meshes for the clothing or armor. All these meshes will be children of the same Armature with the same actions applied to all of them.
|
||||
|
||||
When exporting the Armature, if you select the option: `EX_SHARED_ARMATURE` (Shared Armature), then `blender2ogre` will export the Armature as a single .skeleton file and all the exported OGRE meshes will use the same Skeleton for their animations. (The normal behaviour is for `blender2ogre` to export one skeleton for each mesh that is a child of the Armature)
|
||||
|
||||
Then from you OGRE code use:
|
||||
```
|
||||
// Share Skeleton
|
||||
Ogre::Entity* baseMesh = mSceneMgr->getEntity("Player_Skin");
|
||||
Ogre::Entity* shirtMesh = mSceneMgr->getEntity("Player_Shirt")
|
||||
shirtMesh->shareSkeletonInstanceWith(baseMesh);
|
||||
|
||||
// Attach Entities to Scene Nodes
|
||||
Ogre::SceneNode* playerNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("PlayerNode");
|
||||
playerNode->attachObject(baseMesh);
|
||||
playerNode->attachObject(shirtMesh);
|
||||
```
|
||||
|
||||
Now, when animating the `baseMesh`, the `shirtMesh` will animate in the same manner, giving the illusion that the character has clothing/armor.
|
||||
|
||||
### Base Mesh/Skin clipping
|
||||
Usually in some poses or animations, the base mesh (skin) might clip on top of the clothes, which looks pretty bad.
|
||||
- One solution to this problem is to use shape keys to make the base model thinner and apply the shape key when the character has the clothes on since then the shape won't be visible.
|
||||
- Another option is to use the Blender `Mask` modifier: create a vertex group for every part of the base mesh that will be visible with clothes on (like arms, head and ankles perhaps) and select that vertex group as the mask. With the mask active, only those parts of the mesh will be exported.
|
||||
- A third option is to use vertex groups. Create one vertex group for each part of the base mesh/skin that you want to hide and then you can reference these vertex groups within OGRE to hide them. More details in: [Exporting Custom Vertex Groups](README.md#exporting-custom-vertex-groups)
|
||||
|
||||
## Random Tips from the forum
|
||||
Here are gathered some tips lifted from the forum: [Blender26 Ogre Exporter](https://forums.ogre3d.org/viewtopic.php?f=8&t=61485)
|
||||
|
||||
For combining animations at the same time, you may need to call `setUseBaseKeyFrame()` on the animations to tell OGRE which pose each of the animations should be relative to. By default, Ogre uses the skeleton bind pose.
|
||||
The "base keyframe" should be a pose where the bones that should be untouched (like the top half of the character for the bottom animation) match exactly for ALL frames of the animation. Otherwise, you'll get unwanted extra rotations. So if you aren't setting the base key frame explicitly, then your animations should be authored such that the bones that should be untouched match the bind pose. The top half of the character should be in bind pose for the bottom animation. For details, see [setUseBaseKeyFrame()](https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_animation.html#a22780aed1bf68e2cd67e6dd62ac5c287)
|
||||
|
||||
The editor only exports the local coordinates and ignores any global transformation, unfortunately in Blender a skeleton and the mesh it is attached to can have different global transformations and take these into account when deforming the mesh. The easy solution to this is moving the skeleton in object-mode until it 'looks like' it is placed correctly, then press `Ctrl-A` to apply location, rotation and if necessary scale. This will set the global transform to zero and recalculate every local transformation to match the view. (apply this to both, skeleton and mesh).
|
||||
|
||||
"An object is not allowed to be scaled/rotated/translated before export": since I read that, I asked myself whether this would mean that all objects need to be default size and located in the origin, as created by "Add", which would be of somewhat reduced use. As it turned out, that meant the object is not allowed to have any LOCAL transformations to it. For example, instead of having a cube with dimensions (8, 2, 4) and a scale (0.5, 1.5, 2), one needs a cube of dimensions (4, 3, 8 ) and of scale (1, 1, 1). The same for rotation and location. This can be achieved by selecting the object, pressing `Ctrl-A`, and then selecting first "Location", followed by (after pressing `Ctrl-A` again) "Rotation & Scale". By doing so, all local transformations are applied to the object and reset to safe values.
|
||||
|
||||
Every time you have a problem with skeletons, make sure that the mesh and the skeleton has the scale and rotation applied `Ctrl+A` and share the same position (origin in the same pos). For that you can select the skeleton, `Shift+S -> Cursor to selected`, then select the mesh, `Ctrl+Alt+Chift+C -> Origin to 3D Cursor`. Maybe it will mess up the envelope, so maybe is the other way around (first mesh then skeleton).
|
||||
|
||||
In very few cases, it has happened to me that if I apply an armature to a mesh, and later do the reset to that mesh, the animation in Ogre is a complete mess, in those cases what I had to do is unparent all bones from the mesh, erase the automatic Armature modifier in the "Objects Modifiers" window, and erase the Vertex Groups in the "Object Data" window, just mentioned if this happens to you.
|
||||
|
||||
The standard way of attaching equipment to a character is to attach it to a bone. Either with a constant offset to an existing bone (maybe the spine) or by creating extra non-deformable bones just for the purpose of attaching equipment to them which then can be moved in your animation to move the attached equipment in any way you like. The Sindbad character for example has extra bones at the sheaths on his back to properly place the swords in them when they are not drawn.
|
||||
|
||||
## Automation
|
||||
You might get excited animating your models and ending up with a lot of actions that have no corresponding NLA Track.
|
||||
|
||||
Going through every action, pushing it down into the NLA Track and renaming it is a very tedious process, so here is a simple script to automate that process.
|
||||
```
|
||||
import bpy
|
||||
|
||||
def add_NLA_strips(object):
|
||||
|
||||
for action in bpy.data.actions:
|
||||
track = object.animation_data.nla_tracks.new()
|
||||
track.strips.new(action.name, action.frame_range[0], action)
|
||||
track.name = action.name
|
||||
|
||||
# Example for a single armature:
|
||||
add_NLA_strips(bpy.data.objects['Armature'])
|
||||
|
||||
# If there are many armatures in the scene
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type != 'ARMATURE':
|
||||
continue
|
||||
|
||||
add_NLA_strips(obj)
|
||||
```
|
||||
66
assets/blender/scripts/blender2ogre/SkyBoxes.md
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
# Exporting SkyBoxes
|
||||
|
||||

|
||||
|
||||
## Documentation:
|
||||
- https://en.wikipedia.org/wiki/Skybox_(video_games)
|
||||
- https://ogrecave.github.io/ogre/api/latest/class_ogre_1_1_scene_manager.html#a370c23fff9cc351b150632c707a46700
|
||||
- https://docs.blender.org/manual/en/latest/render/lights/world.html
|
||||
|
||||
## Introduction
|
||||
It is possible to export an HDRi map as a Cube Map that can be used in OGRE for SkyBoxes or environment maps.
|
||||
|
||||
## Setting the Environment Map in Blender
|
||||
1) Go to `World Properties` Tab \
|
||||

|
||||
|
||||
2) Then under "Surface", make sure that `Use Nodes` is checked. Surface should say "Background".
|
||||
|
||||
3) Then, where it says `Color` click on the white dot and a menu should appear to select the texture for the environment map. \
|
||||

|
||||
|
||||
4) Open a Picture or select one from the Blender cache. \
|
||||

|
||||
|
||||
5) Then select `File`->`Export`->`Ogre3D (.scene and .mesh)` \
|
||||

|
||||
|
||||
6) In the `General` Settings, make sure to enable the option `Export SkyBox` and set the resolution to some power of 2 value (e.g.: 512, 1024, 2048, etc.). \
|
||||
The higher the resolution the more time it will take for the exporting process. \
|
||||

|
||||
|
||||
The exporter will create six images, one for each of the faces of the "SkyBox":
|
||||
- front: "<texture_name>_fr.png"
|
||||
- back: "<texture_name>_bk.png"
|
||||
- right: "<texture_name>_rt.png"
|
||||
- left: "<texture_name>_lf.png"
|
||||
- top: "<texture_name>_up.png"
|
||||
- bottom: "<texture_name>_dn.png"
|
||||
\
|
||||

|
||||
|
||||
And a material with the same name as the environment texture.
|
||||
|
||||
## Using the SkyBox in OGRE
|
||||
To load the SkyBox in OGRE it can be done with the DotScene Plugin, where you can use the .scene exported by `blender2ogre`.
|
||||
\
|
||||
The exported scene has the following section:
|
||||
```
|
||||
...
|
||||
<environment >
|
||||
<colourBackground b="0.050876" g="0.050876" r="0.050876" />
|
||||
<skyBox active="true" material="symmetrical_garden_02" />
|
||||
</environment>
|
||||
...
|
||||
```
|
||||
|
||||
To use it in code you can do the following:
|
||||
```
|
||||
String material = "symmetrical_garden_02";
|
||||
Real distance = 5000;
|
||||
bool drawFirst = true;
|
||||
bool active = true;
|
||||
String groupName = "Scene";
|
||||
mSceneMgr->setSkyBox(true, material, distance, drawFirst, rotation, groupName);
|
||||
```
|
||||
171
assets/blender/scripts/blender2ogre/VertexColors.md
Normal file
@@ -0,0 +1,171 @@
|
||||
|
||||
# Exporting Vertex Colors
|
||||
|
||||
## Introduction
|
||||
It is possible to export the vertex color properties of objects in Blender.
|
||||
|
||||
## Creating Vertex Colors in Blender
|
||||
1) Select the object you want to create Vertex Colors for
|
||||
|
||||
2) (Blender version < 3.2) Go the the `Object Data Properties` section and expand the Vertex Colors section \
|
||||

|
||||
|
||||
3) (Blender version >= 3.2) Go the the `Object Data Properties` section and expand the Color Attributes section \
|
||||

|
||||
|
||||
4) Press the + button to create the Vertex Color data
|
||||
|
||||
5) (Blender version < 3.2) \
|
||||

|
||||
|
||||
6) (Blender version >= 3.2) You have to select `Face Corner` and `Byte Color` so that the Color data is compatible with the exporter \
|
||||

|
||||
|
||||
7) Now that we have created the data block, it is time to fill it with something useful.
|
||||
Change the mode to `Vertex Paint` and paint the vertices as you please. \
|
||||

|
||||
|
||||
8) To see the results in Blender you have to create a Material that takes as input the Vertex Colors.
|
||||
Go to the `Shading` tab and create a Material as follows: \
|
||||

|
||||
|
||||
> **NOTE**: In Blender version >= 3.2 the input node is called `Color Attribute`
|
||||
|
||||
9) One last step is to modify the Material so that it is able to render transparency with EEVEE.
|
||||
Go to the `Material Properties` section and set `Blend Mode: Alpha Hashed` and `Shadow Mode: None` for best results \
|
||||

|
||||
|
||||
## About exporting and importing into Ogre
|
||||
The exported vertex color property shows in the resulting mesh XML as follows:
|
||||
```
|
||||
...
|
||||
<sharedgeometry vertexcount="65">
|
||||
<vertexbuffer colours_diffuse="True" normals="true" positions="true" tangent_dimensions="0" tangents="False" texture_coords="1">
|
||||
<vertex>
|
||||
<position x="0.000000" y="-1.000000" z="-1.000000"/>
|
||||
<normal x="0.000000" y="0.447214" z="-0.894427"/>
|
||||
<colour_diffuse value="1.000000 1.000000 1.000000 1.000000"/>
|
||||
<texcoord u="0.250000" v="0.510000"/>
|
||||
</vertex>
|
||||
...
|
||||
```
|
||||
The option `colours_diffuse="True"` is an indication to OGRE and also to the blender2ogre importer that the mesh has vertex colors.
|
||||
The value shows as `<colour_diffuse value="1.000000 1.000000 1.000000 1.000000"/>` as a float4 with RGB values for color and a fourth value for the alpha.
|
||||
|
||||
To view the results in OGRE, the RTSS follows the normal diffuse colour flow, which can track vertex colours and contain alpha.
|
||||
See (vertexcolour): https://ogrecave.github.io/ogre/api/latest/_material-_scripts.html#autotoc_md128
|
||||
|
||||
If you're using your own shaders, then the vertex colour is passed to the vertex shader as the VES_COLOUR (5th element) and format: Colour, typically VET_UBYTE4.
|
||||
https://ogrecave.github.io/ogre/api/latest/group___render_system.html#gac7ecb5ad110f918f709b3c5d5cbae655
|
||||
|
||||
Minimal example `vertex_color.vs`:
|
||||
```
|
||||
#version 330 core
|
||||
|
||||
layout (location = 0) in vec4 vertex;
|
||||
layout (location = 2) in vec3 normal;
|
||||
layout (location = 3) in vec4 colour;
|
||||
|
||||
out vec3 FragPos;
|
||||
out vec3 Normal;
|
||||
out vec4 vertexColor;
|
||||
|
||||
uniform mat4 world;
|
||||
uniform mat4 worldViewProj;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragPos = vec3(world * vertex);
|
||||
Normal = normal;
|
||||
vertexColor = colour;
|
||||
|
||||
gl_Position = worldViewProj * vertex;
|
||||
}
|
||||
```
|
||||
|
||||
Minimal example `vertex_color.fs`:
|
||||
```
|
||||
#version 330 core
|
||||
|
||||
in vec3 FragPos;
|
||||
in vec4 vertexColor;
|
||||
in vec3 vertexNormal;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform vec3 lightPos;
|
||||
uniform vec3 lightColor;
|
||||
uniform vec3 objectColor;
|
||||
|
||||
void main()
|
||||
{
|
||||
// ambient
|
||||
float ambientStrength = 0.3;
|
||||
vec3 ambient = ambientStrength * lightColor;
|
||||
|
||||
// diffuse
|
||||
vec3 norm = normalize(vertexNormal);
|
||||
vec3 lightDir = normalize(lightPos - FragPos);
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
vec3 diffuse = diff * lightColor;
|
||||
|
||||
vec3 result = (ambient + diffuse) * vec3(vertexColor);
|
||||
FragColor = vec4(result, vertexColor.a);
|
||||
}
|
||||
```
|
||||
|
||||
Program:
|
||||
```
|
||||
vertex_program Vertex-ColorVS glsl
|
||||
{
|
||||
source "vertex_color.vs"
|
||||
|
||||
default_params
|
||||
{
|
||||
param_named_auto world world_matrix
|
||||
param_named_auto worldViewProj worldviewproj_matrix
|
||||
}
|
||||
}
|
||||
|
||||
fragment_program Vertex-ColorFS glsl
|
||||
{
|
||||
source "vertex_color.fs"
|
||||
|
||||
default_params
|
||||
{
|
||||
param_named_auto lightPos light_position 0
|
||||
param_named lightColor float3 1.0 1.0 1.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Material:
|
||||
```
|
||||
material VertexColor {
|
||||
receive_shadows on
|
||||
technique {
|
||||
pass {
|
||||
lighting on
|
||||
ambient 0.8 0.8 0.8 1.0
|
||||
diffuse 0.64 0.64 0.64 1.0
|
||||
specular 0.5 0.5 0.5 1.0 12.5
|
||||
emissive 0.0 0.0 0.0 1.0
|
||||
|
||||
//scene_blend one zero
|
||||
|
||||
vertex_program_ref Vertex-ColorVS
|
||||
{
|
||||
}
|
||||
|
||||
fragment_program_ref Vertex-ColorFS
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
Some tips for troubleshooting:
|
||||
- For Blender versions >= 3.2 make sure that the color data is set to `Face Corner` and `Byte Color`.
|
||||
- Beware of selecting an object and directly changing the mode to `Vertex Paint` because the color data is automatically created.
|
||||
201
assets/blender/scripts/blender2ogre/archived_code/game_logic.py
Normal file
@@ -0,0 +1,201 @@
|
||||
## Archived on the 22/09/2021
|
||||
## Original game_logic.py lived at io_ogre/game_logic.py
|
||||
|
||||
_game_logic_intro_doc_ = '''
|
||||
Hijacking the BGE
|
||||
|
||||
Blender contains a fully functional game engine (BGE) that is highly useful for learning the concepts of game programming by breaking it down into three simple parts: Sensor, Controller, and Actuator. An Ogre based game engine will likely have similar concepts in its internal API and game logic scripting. Without a custom interface to define game logic, very often game designers may have to resort to having programmers implement their ideas in purely handwritten script. This is prone to breakage because object names then end up being hard-coded. Not only does this lead to non-reusable code, its also a slow process. Why should we have to resort to this when Blender already contains a very rich interface for game logic? By hijacking a subset of the BGE interface we can make this workflow between game designer and game programmer much better.
|
||||
|
||||
The OgreDocScene format can easily be extened to include extra game logic data. While the BGE contains some features that can not be easily mapped to other game engines, there are many are highly useful generic features we can exploit, including many of the Sensors and Actuators. Blender uses the paradigm of: 1. Sensor -> 2. Controller -> 3. Actuator. In pseudo-code, this can be thought of as: 1. on-event -> 2. conditional logic -> 3. do-action. The designer is most often concerned with the on-events (the Sensors), and the do-actions (the Actuators); and the BGE interface provides a clear way for defining and editing those. Its a harder task to provide a good interface for the conditional logic (Controller), that is flexible enough to fit everyones different Ogre engine and requirements, so that is outside the scope of this exporter at this time. A programmer will still be required to fill the gap between Sensor and Actuator, but hopefully his work is greatly reduced and can write more generic/reuseable code.
|
||||
|
||||
The rules for which Sensors trigger which Actuators is left undefined, as explained above we are hijacking the BGE interface not trying to export and reimplement everything. BGE Controllers and all links are ignored by the exporter, so whats the best way to define Sensor/Actuator relationships? One convention that seems logical is to group Sensors and Actuators by name. More complex syntax could be used in Sensor/Actuators names, or they could be completely ignored and instead all the mapping is done by the game programmer using other rules. This issue is not easily solved so designers and the engine programmers will have to decide upon their own conventions, there is no one size fits all solution.
|
||||
'''
|
||||
|
||||
_ogre_logic_types_doc_ = '''
|
||||
Supported Sensors:
|
||||
. Collision
|
||||
. Near
|
||||
. Radar
|
||||
. Touching
|
||||
. Raycast
|
||||
. Message
|
||||
|
||||
Supported Actuators:
|
||||
. Shape Action*
|
||||
. Edit Object
|
||||
. Camera
|
||||
. Constraint
|
||||
. Message
|
||||
. Motion
|
||||
. Sound
|
||||
. Visibility
|
||||
|
||||
*note: Shape Action
|
||||
The most common thing a designer will want to do is have an event trigger an animation. The BGE contains an Actuator called "Shape Action", with useful properties like: start/end frame, and blending. It also contains a property called "Action" but this is hidden because the exporter ignores action names and instead uses the names of NLA strips when exporting Ogre animation tracks. The current workaround is to hijack the "Frame Property" attribute and change its name to "animation". The designer can then simply type the name of the animation track (NLA strip). Any custom syntax could actually be implemented here for calling animations, its up to the engine programmer to define how this field will be used. For example: "*.explode" could be implemented to mean "on all objects" play the "explode" animation.
|
||||
'''
|
||||
|
||||
# UI panels
|
||||
|
||||
@UI
|
||||
class PANEL_Physics(bpy.types.Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_label = "Physics"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
ob = context.active_object
|
||||
game = ob.game
|
||||
|
||||
if ob.type != 'MESH':
|
||||
return
|
||||
elif ob.subcollision == True:
|
||||
box = layout.box()
|
||||
if ob.parent:
|
||||
box.label(text='object is a collision proxy for: %s' %ob.parent.name)
|
||||
else:
|
||||
box.label(text='WARNING: collision proxy missing parent')
|
||||
return
|
||||
|
||||
box = layout.box()
|
||||
box.prop(ob, 'physics_mode')
|
||||
if ob.physics_mode != 'NONE':
|
||||
box.prop(game, 'mass', text='Mass')
|
||||
box.prop(ob, 'physics_friction', text='Friction', slider=True)
|
||||
box.prop(ob, 'physics_bounce', text='Bounce', slider=True)
|
||||
|
||||
box.label(text="Damping:")
|
||||
box.prop(game, 'damping', text='Translation', slider=True)
|
||||
box.prop(game, 'rotation_damping', text='Rotation', slider=True)
|
||||
|
||||
box.label(text="Velocity:")
|
||||
box.prop(game, "velocity_min", text="Minimum")
|
||||
box.prop(game, "velocity_max", text="Maximum")
|
||||
|
||||
@UI
|
||||
class PANEL_Collision(bpy.types.Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_label = "Collision"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
ob = context.active_object
|
||||
game = ob.game
|
||||
|
||||
if ob.type != 'MESH':
|
||||
return
|
||||
elif ob.subcollision == True:
|
||||
box = layout.box()
|
||||
if ob.parent:
|
||||
box.label(text='object is a collision proxy for: %s' %ob.parent.name)
|
||||
else:
|
||||
box.label(text='WARNING: collision proxy missing parent')
|
||||
return
|
||||
|
||||
mode = ob.collision_mode
|
||||
if mode == 'NONE':
|
||||
box = layout.box()
|
||||
op = box.operator( 'ogre.set_collision', text='Enable Collision', icon='PHYSICS' )
|
||||
op.MODE = 'PRIMITIVE:%s' %game.collision_bounds_type
|
||||
else:
|
||||
prim = game.collision_bounds_type
|
||||
|
||||
box = layout.box()
|
||||
op = box.operator( 'ogre.set_collision', text='Disable Collision', icon='X' )
|
||||
op.MODE = 'NONE'
|
||||
box.prop(game, "collision_margin", text="Collision Margin", slider=True)
|
||||
|
||||
box = layout.box()
|
||||
if mode == 'PRIMITIVE':
|
||||
box.label(text='Primitive: %s' %prim)
|
||||
else:
|
||||
box.label(text='Primitive')
|
||||
|
||||
row = box.row()
|
||||
_icons = {
|
||||
'BOX':'MESH_CUBE', 'SPHERE':'MESH_UVSPHERE', 'CYLINDER':'MESH_CYLINDER',
|
||||
'CONE':'MESH_CONE', 'CAPSULE':'META_CAPSULE'}
|
||||
for a in 'BOX SPHERE CYLINDER CONE CAPSULE'.split():
|
||||
if prim == a and mode == 'PRIMITIVE':
|
||||
op = row.operator( 'ogre.set_collision', text='', icon=_icons[a], emboss=True )
|
||||
op.MODE = 'PRIMITIVE:%s' %a
|
||||
else:
|
||||
op = row.operator( 'ogre.set_collision', text='', icon=_icons[a], emboss=False )
|
||||
op.MODE = 'PRIMITIVE:%s' %a
|
||||
|
||||
box = layout.box()
|
||||
if mode == 'MESH': box.label(text='Mesh: %s' %prim.split('_')[0] )
|
||||
else: box.label(text='Mesh')
|
||||
row = box.row()
|
||||
row.label(text='- - - - - - - - - - - - - -')
|
||||
_icons = {'TRIANGLE_MESH':'MESH_ICOSPHERE', 'CONVEX_HULL':'SURFACE_NCURVE'}
|
||||
for a in 'TRIANGLE_MESH CONVEX_HULL'.split():
|
||||
if prim == a and mode == 'MESH':
|
||||
op = row.operator( 'ogre.set_collision', text='', icon=_icons[a], emboss=True )
|
||||
op.MODE = 'MESH:%s' %a
|
||||
else:
|
||||
op = row.operator( 'ogre.set_collision', text='', icon=_icons[a], emboss=False )
|
||||
op.MODE = 'MESH:%s' %a
|
||||
|
||||
box = layout.box()
|
||||
if mode == 'DECIMATED':
|
||||
box.label(text='Decimate: %s' %prim.split('_')[0] )
|
||||
row = box.row()
|
||||
mod = _get_proxy_decimate_mod( ob )
|
||||
assert mod # decimate modifier is missing
|
||||
row.label(text='Faces: %s' %mod.face_count )
|
||||
box.prop( mod, 'ratio', text='' )
|
||||
else:
|
||||
box.label(text='Decimate')
|
||||
row = box.row()
|
||||
row.label(text='- - - - - - - - - - - - - -')
|
||||
|
||||
_icons = {'TRIANGLE_MESH':'MESH_ICOSPHERE', 'CONVEX_HULL':'SURFACE_NCURVE'}
|
||||
for a in 'TRIANGLE_MESH CONVEX_HULL'.split():
|
||||
if prim == a and mode == 'DECIMATED':
|
||||
op = row.operator( 'ogre.set_collision', text='', icon=_icons[a], emboss=True )
|
||||
op.MODE = 'DECIMATED:%s' %a
|
||||
else:
|
||||
op = row.operator( 'ogre.set_collision', text='', icon=_icons[a], emboss=False )
|
||||
op.MODE = 'DECIMATED:%s' %a
|
||||
|
||||
box = layout.box()
|
||||
if mode == 'TERRAIN':
|
||||
terrain = get_subcollisions( ob )[0]
|
||||
if ob.collision_terrain_x_steps != terrain.collision_terrain_x_steps or ob.collision_terrain_y_steps != terrain.collision_terrain_y_steps:
|
||||
op = box.operator( 'ogre.set_collision', text='Rebuild Terrain', icon='MESH_GRID' )
|
||||
op.MODE = 'TERRAIN'
|
||||
else:
|
||||
box.label(text='Terrain:')
|
||||
row = box.row()
|
||||
row.prop( ob, 'collision_terrain_x_steps', 'X' )
|
||||
row.prop( ob, 'collision_terrain_y_steps', 'Y' )
|
||||
#box.prop( terrain.modifiers[0], 'offset' ) # gets normalized away
|
||||
box.prop( terrain.modifiers[0], 'cull_face', text='Cull' )
|
||||
box.prop( terrain, 'location' ) # TODO hide X and Y
|
||||
else:
|
||||
op = box.operator( 'ogre.set_collision', text='Terrain Collision', icon='MESH_GRID' )
|
||||
op.MODE = 'TERRAIN'
|
||||
|
||||
box = layout.box()
|
||||
if mode == 'COMPOUND':
|
||||
op = box.operator( 'ogre.set_collision', text='Compound Collision', icon='ROTATECOLLECTION' )
|
||||
else:
|
||||
op = box.operator( 'ogre.set_collision', text='Compound Collision', icon='ROTATECOLLECTION' )
|
||||
op.MODE = 'COMPOUND'
|
||||
|
||||
216
assets/blender/scripts/blender2ogre/archived_code/materials.py
Normal file
@@ -0,0 +1,216 @@
|
||||
## Archived on the 22/09/2021
|
||||
## Original materials.py lived at io_ogre/ui/materials.py
|
||||
|
||||
import bpy
|
||||
from .. import shader
|
||||
from bpy.props import IntProperty
|
||||
from ..ogre.material import OgreMaterialGenerator
|
||||
from ..util import wordwrap
|
||||
|
||||
|
||||
def ogre_register(register):
|
||||
yield PANEL_properties_window_ogre_material
|
||||
yield MatPass1
|
||||
yield MatPass2
|
||||
yield MatPass3
|
||||
yield MatPass4
|
||||
yield MatPass5
|
||||
yield MatPass6
|
||||
yield MatPass7
|
||||
yield MatPass8
|
||||
yield MT_preview_material_text
|
||||
yield CreateMaterialLayerOperator
|
||||
yield SetupMaterialPassesOperator
|
||||
|
||||
class MT_preview_material_text(bpy.types.Menu):
|
||||
""" Preview the outputted material in a menu in the top header """
|
||||
bl_label = 'preview'
|
||||
|
||||
@classmethod
|
||||
def poll(self,context):
|
||||
if context.active_object and context.active_object.active_material:
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
mat = context.active_object.active_material
|
||||
if mat:
|
||||
preview = OgreMaterialGenerator( mat ).generate()
|
||||
for line in preview.splitlines():
|
||||
if line.strip():
|
||||
for ww in wordwrap( line ):
|
||||
layout.label(text=ww)
|
||||
|
||||
|
||||
class CreateMaterialLayerOperator(bpy.types.Operator):
|
||||
'''helper to create new material layer'''
|
||||
bl_idname = "ogre.helper_create_attach_material_layer"
|
||||
bl_label = "creates and assigns new material to layer"
|
||||
bl_options = {'REGISTER'}
|
||||
INDEX = IntProperty(name="material layer index", description="index", default=0, min=0, max=8)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.active_object.active_material\
|
||||
and context.active_object.active_material.use_material_passes:
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
mat = context.active_object.active_material
|
||||
nodes = shader.get_or_create_material_passes( mat )
|
||||
node = nodes[ self.INDEX ]
|
||||
node.material = bpy.data.materials.new( name='%s.LAYER%s'%(mat.name,self.INDEX) )
|
||||
node.material.offset_z = (self.INDEX*2) + 2 # nudge each pass by 2
|
||||
return {'FINISHED'}
|
||||
|
||||
class SetupMaterialPassesOperator(bpy.types.Operator):
|
||||
'''operator: enables material nodes (workaround for not having IDPointers in pyRNA)'''
|
||||
bl_idname = "ogre.force_setup_material_passes"
|
||||
bl_label = "force bpyShaders"
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.active_object.active_material: return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
mat = context.active_object.active_material
|
||||
mat.use_material_passes = True
|
||||
shader.create_material_passes( mat )
|
||||
return {'FINISHED'}
|
||||
|
||||
class PANEL_properties_window_ogre_material( bpy.types.Panel ):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "material"
|
||||
bl_label = "Ogre Material (base pass)"
|
||||
|
||||
@classmethod
|
||||
def poll( self, context ):
|
||||
if not hasattr(context, "material"): return False
|
||||
if not context.active_object: return False
|
||||
if not context.active_object.active_material: return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
mat = context.material
|
||||
ob = context.object
|
||||
slot = context.material_slot
|
||||
layout = self.layout
|
||||
if not mat.use_material_passes:
|
||||
box = layout.box()
|
||||
box.operator( 'ogre.force_setup_material_passes', text="Ogre Material Layers", icon='SCENE_DATA' )
|
||||
|
||||
ogre_material_panel( layout, mat )
|
||||
ogre_material_panel_extra( layout, mat )
|
||||
|
||||
class _OgreMatPass( object ):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "material"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.active_object.active_material and context.active_object.active_material.use_material_passes:
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
if not hasattr(context, "material"):
|
||||
return
|
||||
if not context.active_object:
|
||||
return
|
||||
if not context.active_object.active_material:
|
||||
return
|
||||
|
||||
mat = context.material
|
||||
ob = context.object
|
||||
slot = context.material_slot
|
||||
layout = self.layout
|
||||
#layout.label(text=str(self.INDEX))
|
||||
if mat.use_material_passes:
|
||||
db = layout.box()
|
||||
nodes = shader.get_or_create_material_passes( mat )
|
||||
node = nodes[ self.INDEX ]
|
||||
split = db.row()
|
||||
if node.material: split.prop( node.material, 'use_in_ogre_material_pass', text='' )
|
||||
split.prop( node, 'material' )
|
||||
if not node.material:
|
||||
op = split.operator( 'ogre.helper_create_attach_material_layer', icon="PLUS", text='' )
|
||||
op.INDEX = self.INDEX
|
||||
if node.material and node.material.use_in_ogre_material_pass:
|
||||
dbb = db.box()
|
||||
ogre_material_panel( dbb, node.material, parent=mat )
|
||||
ogre_material_panel_extra( dbb, node.material )
|
||||
|
||||
# is there a better way to do this?
|
||||
class MatPass1( _OgreMatPass, bpy.types.Panel ): INDEX = 0; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass2( _OgreMatPass, bpy.types.Panel ): INDEX = 1; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass3( _OgreMatPass, bpy.types.Panel ): INDEX = 2; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass4( _OgreMatPass, bpy.types.Panel ): INDEX = 3; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass5( _OgreMatPass, bpy.types.Panel ): INDEX = 4; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass6( _OgreMatPass, bpy.types.Panel ): INDEX = 5; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass7( _OgreMatPass, bpy.types.Panel ): INDEX = 6; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
class MatPass8( _OgreMatPass, bpy.types.Panel ): INDEX = 7; bl_label = "Ogre Material (pass%s)"%str(INDEX+1)
|
||||
|
||||
def ogre_material_panel_extra( parent, mat ):
|
||||
box = parent.box()
|
||||
header = box.row()
|
||||
header.prop(mat, 'use_ogre_advanced_options', text='---guru options---' )
|
||||
|
||||
if mat.use_ogre_advanced_options:
|
||||
box.prop(mat, 'offset_z')
|
||||
for tag in 'ogre_colour_write ogre_normalise_normals ogre_light_clip_planes ogre_light_scissor ogre_alpha_to_coverage ogre_depth_check'.split():
|
||||
box.prop(mat, tag)
|
||||
for tag in 'ogre_polygon_mode ogre_shading ogre_transparent_sorting ogre_illumination_stage ogre_depth_func ogre_scene_blend_op'.split():
|
||||
box.prop(mat, tag)
|
||||
|
||||
def ogre_material_panel( layout, mat, parent=None, show_programs=True ):
|
||||
if not parent:
|
||||
return # only allow on pass1 and higher
|
||||
|
||||
box = layout.box()
|
||||
header = box.row()
|
||||
|
||||
header.prop(mat, 'use_ogre_parent_material', icon='FILE_SCRIPT', text='')
|
||||
|
||||
if mat.use_ogre_parent_material:
|
||||
row = box.row()
|
||||
row.prop(mat, 'ogre_parent_material', text='')
|
||||
|
||||
s = get_ogre_user_material( mat.ogre_parent_material ) # gets by name
|
||||
if s and (s.vertex_programs or s.fragment_programs):
|
||||
progs = s.get_programs()
|
||||
split = box.row()
|
||||
texnodes = None
|
||||
|
||||
if parent:
|
||||
texnodes = shader.get_texture_subnodes( parent, submaterial=mat )
|
||||
elif mat.node_tree:
|
||||
texnodes = shader.get_texture_subnodes( mat ) # assume toplevel
|
||||
|
||||
if not progs:
|
||||
bx = split.box()
|
||||
bx.label( text='(missing shader programs)', icon='ERROR' )
|
||||
elif s.texture_units and texnodes:
|
||||
bx = split.box()
|
||||
for i,name in enumerate(s.texture_units_order):
|
||||
if i<len(texnodes):
|
||||
row = bx.row()
|
||||
#row.label( text=name )
|
||||
tex = texnodes[i]
|
||||
row.prop( tex, 'texture', text=name )
|
||||
if parent:
|
||||
inputs = shader.get_connected_input_nodes( parent, tex )
|
||||
if inputs:
|
||||
geo = inputs[0]
|
||||
assert geo.type == 'GEOMETRY'
|
||||
row.prop( geo, 'uv_layer', text='UV' )
|
||||
else:
|
||||
print('WARNING: no slot for texture unit:', name)
|
||||
|
||||
if show_programs and (s.vertex_programs or s.fragment_programs):
|
||||
bx = box.box()
|
||||
for name in s.vertex_programs:
|
||||
bx.label( text=name )
|
||||
for name in s.fragment_programs:
|
||||
bx.label( text=name )
|
||||
229
assets/blender/scripts/blender2ogre/archived_code/terrain.py
Normal file
@@ -0,0 +1,229 @@
|
||||
## Archived on the 22/09/2021
|
||||
## Original terrain.py lived at io_ogre/terrain.py
|
||||
|
||||
import bpy
|
||||
|
||||
def _get_proxy_decimate_mod( ob ):
|
||||
proxy = None
|
||||
for child in ob.children:
|
||||
if child.subcollision and child.name.startswith('DECIMATED'):
|
||||
for mod in child.modifiers:
|
||||
if mod.type == 'DECIMATE':
|
||||
return mod
|
||||
|
||||
def bake_terrain( ob, normalize=True ):
|
||||
assert ob.collision_mode == 'TERRAIN'
|
||||
terrain = None
|
||||
for child in ob.children:
|
||||
if child.subcollision and child.name.startswith('TERRAIN'):
|
||||
terrain = child
|
||||
break
|
||||
assert terrain
|
||||
data = terrain.to_mesh(bpy.context.scene, True, "PREVIEW")
|
||||
raw = [ v.co.z for v in data.vertices ]
|
||||
Zmin = min( raw )
|
||||
Zmax = max( raw )
|
||||
depth = Zmax-Zmin
|
||||
m = 1.0 / depth
|
||||
|
||||
rows = []
|
||||
i = 0
|
||||
for x in range( ob.collision_terrain_x_steps ):
|
||||
row = []
|
||||
for y in range( ob.collision_terrain_y_steps ):
|
||||
v = data.vertices[ i ]
|
||||
if normalize:
|
||||
z = (v.co.z - Zmin) * m
|
||||
else:
|
||||
z = v.co.z
|
||||
row.append( z )
|
||||
i += 1
|
||||
if x%2:
|
||||
row.reverse() # blender grid prim zig-zags
|
||||
rows.append( row )
|
||||
return {'data':rows, 'min':Zmin, 'max':Zmax, 'depth':depth}
|
||||
|
||||
def save_terrain_as_NTF( path, ob ): # Tundra format - hardcoded 16x16 patch format
|
||||
info = bake_terrain( ob )
|
||||
url = os.path.join( path, '%s.ntf' % clean_object_name(ob.data.name) )
|
||||
f = open(url, "wb")
|
||||
# Header
|
||||
buf = array.array("I")
|
||||
xs = ob.collision_terrain_x_steps
|
||||
ys = ob.collision_terrain_y_steps
|
||||
xpatches = int(xs/16)
|
||||
ypatches = int(ys/16)
|
||||
header = [ xpatches, ypatches ]
|
||||
buf.fromlist( header )
|
||||
buf.tofile(f)
|
||||
# Body
|
||||
rows = info['data']
|
||||
for x in range( xpatches ):
|
||||
for y in range( ypatches ):
|
||||
patch = []
|
||||
for i in range(16):
|
||||
for j in range(16):
|
||||
v = rows[ (x*16)+i ][ (y*16)+j ]
|
||||
patch.append( v )
|
||||
buf = array.array("f")
|
||||
buf.fromlist( patch )
|
||||
buf.tofile(f)
|
||||
f.close()
|
||||
path,name = os.path.split(url)
|
||||
R = {
|
||||
'url':url, 'min':info['min'], 'max':info['max'], 'path':path, 'name':name,
|
||||
'xpatches': xpatches, 'ypatches': ypatches,
|
||||
'depth':info['depth'],
|
||||
}
|
||||
return R
|
||||
|
||||
class OgreCollisionOp(bpy.types.Operator):
|
||||
'''Ogre Collision'''
|
||||
bl_idname = "ogre.set_collision"
|
||||
bl_label = "modify collision"
|
||||
bl_options = {'REGISTER'}
|
||||
MODE = StringProperty(name="toggle mode", maxlen=32, default="disable")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.active_object.type == 'MESH':
|
||||
return True
|
||||
|
||||
def get_subcollisions( self, ob, create=True ):
|
||||
r = get_subcollisions( ob )
|
||||
if not r and create:
|
||||
# method = getattr(self, 'create_%s'%ob.collision_mode)
|
||||
# p = method(ob)
|
||||
p.name = '%s.%s' %(ob.collision_mode, ob.name)
|
||||
p.subcollision = True
|
||||
r.append( p )
|
||||
return r
|
||||
|
||||
def create_DECIMATED(self, ob):
|
||||
child = ob.copy()
|
||||
bpy.context.scene.collection.objects.link( child )
|
||||
child.matrix_local = mathutils.Matrix()
|
||||
child.parent = ob
|
||||
child.hide_select = True
|
||||
child.draw_type = 'WIRE'
|
||||
#child.select = False
|
||||
child.lock_location = [True]*3
|
||||
child.lock_rotation = [True]*3
|
||||
child.lock_scale = [True]*3
|
||||
decmod = child.modifiers.new('proxy', type='DECIMATE')
|
||||
decmod.ratio = 0.5
|
||||
return child
|
||||
|
||||
def create_TERRAIN(self, ob):
|
||||
x = ob.collision_terrain_x_steps
|
||||
y = ob.collision_terrain_y_steps
|
||||
#################################
|
||||
#pos = ob.matrix_world.to_translation()
|
||||
bpy.ops.mesh.primitive_grid_add(
|
||||
x_subdivisions=x,
|
||||
y_subdivisions=y,
|
||||
size=1.0 ) #, location=pos )
|
||||
grid = bpy.context.active_object
|
||||
assert grid.name.startswith('Grid')
|
||||
grid.collision_terrain_x_steps = x
|
||||
grid.collision_terrain_y_steps = y
|
||||
#############################
|
||||
x,y,z = ob.dimensions
|
||||
sx,sy,sz = ob.scale
|
||||
x *= 1.0/sx
|
||||
y *= 1.0/sy
|
||||
z *= 1.0/sz
|
||||
grid.scale.x = x/2
|
||||
grid.scale.y = y/2
|
||||
grid.location.z -= z/2
|
||||
grid.data.show_all_edges = True
|
||||
grid.draw_type = 'WIRE'
|
||||
grid.hide_select = True
|
||||
#grid.select = False
|
||||
grid.lock_location = [True]*3
|
||||
grid.lock_rotation = [True]*3
|
||||
grid.lock_scale = [True]*3
|
||||
grid.parent = ob
|
||||
bpy.context.scene.objects.active = ob
|
||||
mod = grid.modifiers.new(name='temp', type='SHRINKWRAP')
|
||||
mod.wrap_method = 'PROJECT'
|
||||
mod.use_project_z = True
|
||||
mod.target = ob
|
||||
mod.cull_face = 'FRONT'
|
||||
return grid
|
||||
|
||||
def invoke(self, context, event):
|
||||
ob = context.active_object
|
||||
game = ob.game
|
||||
subtype = None
|
||||
|
||||
if ':' in self.MODE:
|
||||
mode, subtype = self.MODE.split(':')
|
||||
##BLENDERBUG##ob.game.collision_bounds_type = subtype # BUG this can not come before
|
||||
if subtype in 'BOX SPHERE CYLINDER CONE CAPSULE'.split():
|
||||
ob.draw_bounds_type = subtype
|
||||
else:
|
||||
ob.draw_bounds_type = 'POLYHEDRON'
|
||||
ob.game.collision_bounds_type = subtype # BLENDERBUG - this must come after draw_bounds_type assignment
|
||||
else:
|
||||
mode = self.MODE
|
||||
ob.collision_mode = mode
|
||||
|
||||
if ob.data.show_all_edges:
|
||||
ob.data.show_all_edges = False
|
||||
if ob.show_texture_space:
|
||||
ob.show_texture_space = False
|
||||
if ob.show_bounds:
|
||||
ob.show_bounds = False
|
||||
if ob.show_wire:
|
||||
ob.show_wire = False
|
||||
for child in ob.children:
|
||||
if child.subcollision and not child.hide_viewport:
|
||||
child.hide_viewport = True
|
||||
|
||||
if mode == 'NONE':
|
||||
game.use_ghost = True
|
||||
game.use_collision_bounds = False
|
||||
elif mode == 'PRIMITIVE':
|
||||
game.use_ghost = False
|
||||
game.use_collision_bounds = True
|
||||
ob.show_bounds = True
|
||||
elif mode == 'MESH':
|
||||
game.use_ghost = False
|
||||
game.use_collision_bounds = True
|
||||
ob.show_wire = True
|
||||
if game.collision_bounds_type == 'CONVEX_HULL':
|
||||
ob.show_texture_space = True
|
||||
else:
|
||||
ob.data.show_all_edges = True
|
||||
elif mode == 'DECIMATED':
|
||||
game.use_ghost = True
|
||||
game.use_collision_bounds = False
|
||||
game.use_collision_compound = True
|
||||
proxy = self.get_subcollisions(ob)[0]
|
||||
if proxy.hide_viewport: proxy.hide_viewport = False
|
||||
ob.game.use_collision_compound = True # proxy
|
||||
mod = _get_proxy_decimate_mod( ob )
|
||||
mod.show_viewport = True
|
||||
if not proxy.select: # ugly (but works)
|
||||
proxy.hide_select = False
|
||||
proxy.select = True
|
||||
proxy.hide_select = True
|
||||
if game.collision_bounds_type == 'CONVEX_HULL':
|
||||
ob.show_texture_space = True
|
||||
elif mode == 'TERRAIN':
|
||||
game.use_ghost = True
|
||||
game.use_collision_bounds = False
|
||||
game.use_collision_compound = True
|
||||
proxy = self.get_subcollisions(ob)[0]
|
||||
if proxy.hide_viewport:
|
||||
proxy.hide_viewport = False
|
||||
elif mode == 'COMPOUND':
|
||||
game.use_ghost = True
|
||||
game.use_collision_bounds = False
|
||||
game.use_collision_compound = True
|
||||
else:
|
||||
assert 0 # unknown mode
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
398
assets/blender/scripts/blender2ogre/archived_code/todo.py
Normal file
@@ -0,0 +1,398 @@
|
||||
## Archived on the 22/09/2021
|
||||
## Original todo.py lived at io_ogre/todo.py
|
||||
|
||||
## Blender world panel options for EC_SkyX creation
|
||||
## todo: EC_SkyX has changes a bit lately, see that
|
||||
## all these options are still correct and valid
|
||||
## old todo (?): Move to tundra.py
|
||||
|
||||
#@UI
|
||||
class OgreSkyPanel(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "world"
|
||||
bl_label = "Ogre Sky Settings"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
box = layout.box()
|
||||
box.prop( context.world, 'ogre_skyX' )
|
||||
if context.world.ogre_skyX:
|
||||
box.prop( context.world, 'ogre_skyX_time' )
|
||||
box.prop( context.world, 'ogre_skyX_wind' )
|
||||
box.prop( context.world, 'ogre_skyX_volumetric_clouds' )
|
||||
if context.world.ogre_skyX_volumetric_clouds:
|
||||
box.prop( context.world, 'ogre_skyX_cloud_density_x' )
|
||||
box.prop( context.world, 'ogre_skyX_cloud_density_y' )
|
||||
|
||||
#@UI
|
||||
class PANEL_Object(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "object"
|
||||
bl_label = "Object+"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
ob = context.active_object
|
||||
layout = self.layout
|
||||
box = layout.box()
|
||||
box.prop( ob, 'cast_shadows' )
|
||||
|
||||
box.prop( ob, 'use_draw_distance' )
|
||||
if ob.use_draw_distance:
|
||||
box.prop( ob, 'draw_distance' )
|
||||
#if ob.find_armature():
|
||||
if ob.type == 'EMPTY':
|
||||
box.prop( ob, 'use_avatar' )
|
||||
box.prop( ob, 'avatar_reference' )
|
||||
|
||||
#@UI
|
||||
class PANEL_Speaker(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "data"
|
||||
bl_label = "Sound+"
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.active_object.type=='SPEAKER': return True
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
box = layout.box()
|
||||
box.prop( context.active_object.data, 'play_on_load' )
|
||||
box.prop( context.active_object.data, 'loop' )
|
||||
box.prop( context.active_object.data, 'use_spatial' )
|
||||
|
||||
#@UI
|
||||
class PANEL_MultiResLOD(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "modifier"
|
||||
bl_label = "Multi-Resolution LOD"
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.active_object and context.active_object.type=='MESH':
|
||||
ob = context.active_object
|
||||
if ob.modifiers and ob.modifiers[0].type=='MULTIRES':
|
||||
return True
|
||||
def draw(self, context):
|
||||
ob = context.active_object
|
||||
layout = self.layout
|
||||
box = layout.box()
|
||||
box.prop( ob, 'use_multires_lod' )
|
||||
if ob.use_multires_lod:
|
||||
box.prop( ob, 'multires_lod_range' )
|
||||
|
||||
@UI
|
||||
class PANEL_node_editor_ui_extra( bpy.types.Panel ):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_label = "Ogre Material Advanced"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
@classmethod
|
||||
def poll(self,context):
|
||||
if context.space_data.id: return True
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
topmat = context.space_data.id # the top level node_tree
|
||||
mat = topmat.active_node_material # the currently selected sub-material
|
||||
if mat:
|
||||
self.bl_label = mat.name + ' (advanced)'
|
||||
ogre_material_panel_extra( layout, mat )
|
||||
else:
|
||||
self.bl_label = topmat.name + ' (advanced)'
|
||||
ogre_material_panel_extra( layout, topmat )
|
||||
|
||||
|
||||
@UI
|
||||
class PANEL_node_editor_ui( bpy.types.Panel ):
|
||||
bl_space_type = 'NODE_EDITOR'
|
||||
bl_region_type = 'UI'
|
||||
bl_label = "Ogre Material"
|
||||
|
||||
@classmethod
|
||||
def poll(self,context):
|
||||
if context.space_data.id:
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
topmat = context.space_data.id # the top level node_tree
|
||||
mat = topmat.active_node_material # the currently selected sub-material
|
||||
if not mat or topmat.name == mat.name:
|
||||
self.bl_label = topmat.name
|
||||
if not topmat.use_material_passes:
|
||||
layout.operator(
|
||||
'ogre.force_setup_material_passes',
|
||||
text="Ogre Material Layers",
|
||||
icon='SCENE_DATA'
|
||||
)
|
||||
ogre_material_panel( layout, topmat, show_programs=False )
|
||||
elif mat:
|
||||
self.bl_label = mat.name
|
||||
ogre_material_panel( layout, mat, topmat, show_programs=False )
|
||||
#@UI
|
||||
class PANEL_Configure(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "scene"
|
||||
bl_label = "Ogre Configuration File"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
op = layout.operator( 'ogre.save_config', text='update config file', icon='FILE' )
|
||||
for tag in _CONFIG_TAGS_:
|
||||
layout.prop( context.window_manager, tag )
|
||||
|
||||
#@UI
|
||||
class PANEL_Textures(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "texture"
|
||||
bl_label = "Ogre Texture"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not hasattr(context, "texture_slot"):
|
||||
return False
|
||||
else: return True
|
||||
|
||||
def draw(self, context):
|
||||
#if not hasattr(context, "texture_slot"):
|
||||
# return False
|
||||
layout = self.layout
|
||||
#idblock = context_tex_datablock(context)
|
||||
slot = context.texture_slot
|
||||
if not slot or not slot.texture:
|
||||
return
|
||||
|
||||
btype = slot.blend_type # todo: fix this hack if/when slots support pyRNA
|
||||
ex = False; texop = None
|
||||
if btype in TextureUnit.colour_op:
|
||||
if btype=='MIX' and slot.use_map_alpha and not slot.use_stencil:
|
||||
if slot.diffuse_color_factor >= 1.0:
|
||||
texop = 'alpha_blend'
|
||||
else:
|
||||
texop = TextureUnit.colour_op_ex[ btype ]
|
||||
ex = True
|
||||
elif btype=='MIX' and slot.use_map_alpha and slot.use_stencil:
|
||||
texop = 'blend_current_alpha'; ex=True
|
||||
elif btype=='MIX' and not slot.use_map_alpha and slot.use_stencil:
|
||||
texop = 'blend_texture_alpha'; ex=True
|
||||
else:
|
||||
texop = TextureUnit.colour_op[ btype ]
|
||||
elif btype in TextureUnit.colour_op_ex:
|
||||
texop = TextureUnit.colour_op_ex[ btype ]
|
||||
ex = True
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
if texop:
|
||||
if ex:
|
||||
row.prop(slot, "blend_type", text=texop, icon='NEW')
|
||||
else:
|
||||
row.prop(slot, "blend_type", text=texop)
|
||||
else:
|
||||
row.prop(slot, "blend_type", text='(invalid option)')
|
||||
|
||||
if btype == 'MIX':
|
||||
row.prop(slot, "use_stencil", text="")
|
||||
row.prop(slot, "use_map_alpha", text="")
|
||||
if texop == 'blend_manual':
|
||||
row = box.row()
|
||||
row.label(text="Alpha:")
|
||||
row.prop(slot, "diffuse_color_factor", text="")
|
||||
|
||||
if hasattr(slot.texture, 'image') and slot.texture.image:
|
||||
row = box.row()
|
||||
n = '(invalid option)'
|
||||
if slot.texture.extension in TextureUnit.tex_address_mode:
|
||||
n = TextureUnit.tex_address_mode[ slot.texture.extension ]
|
||||
row.prop(slot.texture, "extension", text=n)
|
||||
if slot.texture.extension == 'CLIP':
|
||||
row.prop(slot, "color", text="Border Color")
|
||||
|
||||
row = box.row()
|
||||
if slot.texture_coords == 'UV':
|
||||
row.prop(slot, "texture_coords", text="", icon='GROUP_UVS')
|
||||
row.prop(slot, "uv_layer", text='Layer')
|
||||
elif slot.texture_coords == 'REFLECTION':
|
||||
row.prop(slot, "texture_coords", text="", icon='MOD_UVPROJECT')
|
||||
n = '(invalid option)'
|
||||
if slot.mapping in 'FLAT SPHERE'.split(): n = ''
|
||||
row.prop(slot, "mapping", text=n)
|
||||
else:
|
||||
row.prop(slot, "texture_coords", text="(invalid mapping option)")
|
||||
|
||||
# Animation and offset options
|
||||
split = layout.row()
|
||||
box = split.box()
|
||||
box.prop(slot, "offset", text="XY=offset, Z=rotation")
|
||||
box = split.box()
|
||||
box.prop(slot, "scale", text="XY=scale (Z ignored)")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text='scrolling animation')
|
||||
|
||||
# Can't use if its enabled by default row.prop(slot, "use_map_density", text="")
|
||||
row.prop(slot, "use_map_scatter", text="")
|
||||
row = box.row()
|
||||
row.prop(slot, "density_factor", text="X")
|
||||
row.prop(slot, "emission_factor", text="Y")
|
||||
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text='rotation animation')
|
||||
row.prop(slot, "emission_color_factor", text="")
|
||||
row.prop(slot, "use_from_dupli", text="")
|
||||
|
||||
## Image magick
|
||||
if hasattr(slot.texture, 'image') and slot.texture.image:
|
||||
img = slot.texture.image
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.prop( img, 'use_convert_format' )
|
||||
if img.use_convert_format:
|
||||
row.prop( img, 'convert_format' )
|
||||
if img.convert_format == 'jpg':
|
||||
box.prop( img, 'jpeg_quality' )
|
||||
|
||||
row = box.row()
|
||||
row.prop( img, 'use_color_quantize', text='Reduce Colors' )
|
||||
if img.use_color_quantize:
|
||||
row.prop( img, 'use_color_quantize_dither', text='dither' )
|
||||
row.prop( img, 'color_quantize', text='colors' )
|
||||
|
||||
row = box.row()
|
||||
row.prop( img, 'use_resize_half' )
|
||||
if not img.use_resize_half:
|
||||
row.prop( img, 'use_resize_absolute' )
|
||||
if img.use_resize_absolute:
|
||||
row = box.row()
|
||||
row.prop( img, 'resize_x' )
|
||||
row.prop( img, 'resize_y' )
|
||||
|
||||
## Ogre Documentation to UI
|
||||
|
||||
class INFO_MT_ogre_shader_pass_attributes(bpy.types.Menu):
|
||||
bl_label = "Shader-Pass"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for cls in _OGRE_SHADER_REF_:
|
||||
layout.menu( cls.__name__ )
|
||||
|
||||
class INFO_MT_ogre_shader_texture_attributes(bpy.types.Menu):
|
||||
bl_label = "Shader-Texture"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for cls in _OGRE_SHADER_REF_TEX_:
|
||||
layout.menu( cls.__name__ )
|
||||
|
||||
class MeshMagick(object):
|
||||
''' Usage: MeshMagick [global_options] toolname [tool_options] infile(s) -- [outfile(s)]
|
||||
Available Tools
|
||||
===============
|
||||
info - print information about the mesh.
|
||||
meshmerge - Merge multiple submeshes into a single mesh.
|
||||
optimise - Optimise meshes and skeletons.
|
||||
rename - Rename different elements of meshes and skeletons.
|
||||
transform - Scale, rotate or otherwise transform a mesh.
|
||||
'''
|
||||
|
||||
@staticmethod
|
||||
def get_merge_group( ob ):
|
||||
return get_merge_group( ob, prefix='magicmerge' )
|
||||
|
||||
@staticmethod
|
||||
def merge( group, path='/tmp', force_name=None ):
|
||||
print('-'*80)
|
||||
print(' mesh magick - merge ')
|
||||
exe = CONFIG['OGRETOOLS_MESH_MAGICK']
|
||||
if not os.path.isfile( exe ):
|
||||
print( 'ERROR: can not find MeshMagick.exe' )
|
||||
print( exe )
|
||||
return
|
||||
|
||||
files = []
|
||||
for ob in group.objects:
|
||||
if ob.data.users == 1: # single users only
|
||||
files.append( os.path.join( path, ob.data.name+'.mesh' ) )
|
||||
print( files[-1] )
|
||||
|
||||
opts = 'meshmerge'
|
||||
if sys.platform == 'linux2': cmd = '/usr/bin/wine %s %s' %(exe, opts)
|
||||
else: cmd = '%s %s' %(exe, opts)
|
||||
if force_name: output = force_name + '.mesh'
|
||||
else: output = '_%s_.mesh' %group.name
|
||||
cmd = cmd.split() + files + ['--', os.path.join(path,output) ]
|
||||
subprocess.call( cmd )
|
||||
print(' mesh magick - complete ')
|
||||
print('-'*80)
|
||||
|
||||
## Selector extras
|
||||
|
||||
class INFO_MT_instances(bpy.types.Menu):
|
||||
bl_label = "Instances"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
inst = gather_instances()
|
||||
for data in inst:
|
||||
ob = inst[data][0]
|
||||
op = layout.operator(INFO_MT_instance.bl_idname, text=ob.name) # operator has no variable for button name?
|
||||
op.mystring = ob.name
|
||||
layout.separator()
|
||||
|
||||
class INFO_MT_instance(bpy.types.Operator):
|
||||
'''select instance group'''
|
||||
bl_idname = "ogre.select_instances"
|
||||
bl_label = "Select Instance Group"
|
||||
bl_options = {'REGISTER', 'UNDO'} # Options for this panel type
|
||||
mystring= StringProperty(name="MyString", description="hidden string", maxlen=1024, default="my string")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
print( 'invoke select_instances op', event )
|
||||
select_instances( context, self.mystring )
|
||||
return {'FINISHED'}
|
||||
|
||||
class INFO_MT_groups(bpy.types.Menu):
|
||||
bl_label = "Groups"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
for group in bpy.data.collections:
|
||||
op = layout.operator(INFO_MT_group.bl_idname, text=group.name) # operator no variable for button name?
|
||||
op.mystring = group.name
|
||||
layout.separator()
|
||||
|
||||
class INFO_MT_group(bpy.types.Operator):
|
||||
'''select group'''
|
||||
bl_idname = "ogre.select_group"
|
||||
bl_label = "Select Group"
|
||||
bl_options = {'REGISTER'} # Options for this panel type
|
||||
mystring= StringProperty(name="MyString", description="hidden string", maxlen=1024, default="my string")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
select_group( context, self.mystring )
|
||||
return {'FINISHED'}
|
||||
|
||||
## More UI
|
||||
|
||||
159
assets/blender/scripts/blender2ogre/archived_code/unused.py
Normal file
@@ -0,0 +1,159 @@
|
||||
## Archived on the 22/09/2021
|
||||
## Original unused.py lived at io_ogre/unused.py
|
||||
|
||||
## Ogre Command Line Tools Documentation
|
||||
## Pop up dialog for various info/error messages
|
||||
|
||||
popup_message = ""
|
||||
|
||||
class PopUpDialogOperator(bpy.types.Operator):
|
||||
bl_idname = "object.popup_dialog_operator"
|
||||
bl_label = "blender2ogre"
|
||||
|
||||
def __init__(self):
|
||||
print("dialog Start")
|
||||
|
||||
def __del__(self):
|
||||
print("dialog End")
|
||||
|
||||
def execute(self, context):
|
||||
print ("execute")
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def draw(self, context):
|
||||
# todo: Make this bigger and center on screen.
|
||||
# Blender UI stuff seems quite complex, would
|
||||
# think that showing a dialog with a message thath
|
||||
# does not hide when mouse is moved would be simpler!
|
||||
global popup_message
|
||||
layout = self.layout
|
||||
col = layout.column()
|
||||
col.label(popup_message, 'ERROR')
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
wm.invoke_popup(self)
|
||||
wm.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event):
|
||||
# Close
|
||||
if event.type == 'LEFTMOUSE':
|
||||
print ("Left mouse")
|
||||
return {'FINISHED'}
|
||||
# Close
|
||||
elif event.type in ('RIGHTMOUSE', 'ESC'):
|
||||
print ("right mouse")
|
||||
return {'FINISHED'}
|
||||
|
||||
print("running modal")
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def show_dialog(message):
|
||||
global popup_message
|
||||
popup_message = message
|
||||
bpy.ops.object.popup_dialog_operator('INVOKE_DEFAULT')
|
||||
|
||||
|
||||
_ogre_command_line_tools_doc = '''
|
||||
Usage: OgreXMLConverter [options] sourcefile [destfile]
|
||||
|
||||
Available options:
|
||||
-i = interactive mode - prompt for options
|
||||
(The next 4 options are only applicable when converting XML to Mesh)
|
||||
-l lodlevels = number of LOD levels
|
||||
-v lodvalue = value increment to reduce LOD
|
||||
-s lodstrategy = LOD strategy to use for this mesh
|
||||
-p lodpercent = Percentage triangle reduction amount per LOD
|
||||
-f lodnumtris = Fixed vertex reduction per LOD
|
||||
-e = DON'T generate edge lists (for stencil shadows)
|
||||
-r = DON'T reorganise vertex buffers to OGRE recommended format.
|
||||
-t = Generate tangents (for normal mapping)
|
||||
-td [uvw|tangent]
|
||||
= Tangent vertex semantic destination (default tangent)
|
||||
-ts [3|4] = Tangent size (3 or 4 components, 4 includes parity, default 3)
|
||||
-tm = Split tangent vertices at UV mirror points
|
||||
-tr = Split tangent vertices where basis is rotated > 90 degrees
|
||||
-o = DON'T optimise out redundant tracks & keyframes
|
||||
-d3d = Prefer D3D packed colour formats (default on Windows)
|
||||
-gl = Prefer GL packed colour formats (default on non-Windows)
|
||||
-E endian = Set endian mode 'big' 'little' or 'native' (default)
|
||||
-x num = Generate no more than num eXtremes for every submesh (default 0)
|
||||
-q = Quiet mode, less output
|
||||
-log filename = name of the log file (default: 'OgreXMLConverter.log')
|
||||
sourcefile = name of file to convert
|
||||
destfile = optional name of file to write to. If you don't
|
||||
specify this OGRE works it out through the extension
|
||||
and the XML contents if the source is XML. For example
|
||||
test.mesh becomes test.xml, test.xml becomes test.mesh
|
||||
if the XML document root is <mesh> etc.
|
||||
'''
|
||||
|
||||
class CMesh(object):
|
||||
|
||||
def __init__(self, data):
|
||||
self.numVerts = N = len( data.vertices )
|
||||
self.numFaces = Nfaces = len(data.tessfaces)
|
||||
|
||||
self.vertex_positions = (ctypes.c_float * (N * 3))()
|
||||
data.vertices.foreach_get( 'co', self.vertex_positions )
|
||||
v = self.vertex_positions
|
||||
|
||||
self.vertex_normals = (ctypes.c_float * (N * 3))()
|
||||
data.vertices.foreach_get( 'normal', self.vertex_normals )
|
||||
|
||||
self.faces = (ctypes.c_uint * (Nfaces * 4))()
|
||||
data.tessfaces.foreach_get( 'vertices_raw', self.faces )
|
||||
|
||||
self.faces_normals = (ctypes.c_float * (Nfaces * 3))()
|
||||
data.tessfaces.foreach_get( 'normal', self.faces_normals )
|
||||
|
||||
self.faces_smooth = (ctypes.c_bool * Nfaces)()
|
||||
data.tessfaces.foreach_get( 'use_smooth', self.faces_smooth )
|
||||
|
||||
self.faces_material_index = (ctypes.c_ushort * Nfaces)()
|
||||
data.tessfaces.foreach_get( 'material_index', self.faces_material_index )
|
||||
|
||||
self.vertex_colors = []
|
||||
if len( data.vertex_colors ):
|
||||
vc = data.vertex_colors[0]
|
||||
n = len(vc.data)
|
||||
# no colors_raw !!?
|
||||
self.vcolors1 = (ctypes.c_float * (n * 3))() # face1
|
||||
vc.data.foreach_get( 'color1', self.vcolors1 )
|
||||
self.vertex_colors.append( self.vcolors1 )
|
||||
|
||||
self.vcolors2 = (ctypes.c_float * (n * 3))() # face2
|
||||
vc.data.foreach_get( 'color2', self.vcolors2 )
|
||||
self.vertex_colors.append( self.vcolors2 )
|
||||
|
||||
self.vcolors3 = (ctypes.c_float * (n * 3))() # face3
|
||||
vc.data.foreach_get( 'color3', self.vcolors3 )
|
||||
self.vertex_colors.append( self.vcolors3 )
|
||||
|
||||
self.vcolors4 = (ctypes.c_float * (n * 3))() # face4
|
||||
vc.data.foreach_get( 'color4', self.vcolors4 )
|
||||
self.vertex_colors.append( self.vcolors4 )
|
||||
|
||||
self.uv_textures = []
|
||||
if data.uv_textures.active:
|
||||
for layer in data.uv_textures:
|
||||
n = len(layer.data) * 8
|
||||
a = (ctypes.c_float * n)()
|
||||
layer.data.foreach_get( 'uv_raw', a ) # 4 faces
|
||||
self.uv_textures.append( a )
|
||||
|
||||
def save( blenderobject, path ):
|
||||
cmesh = Mesh( blenderobject.data )
|
||||
start = time.time()
|
||||
dotmesh(
|
||||
path,
|
||||
ctypes.addressof( cmesh.faces ),
|
||||
ctypes.addressof( cmesh.faces_smooth ),
|
||||
ctypes.addressof( cmesh.faces_material_index ),
|
||||
ctypes.addressof( cmesh.vertex_positions ),
|
||||
ctypes.addressof( cmesh.vertex_normals ),
|
||||
cmesh.numFaces,
|
||||
cmesh.numVerts,
|
||||
)
|
||||
print('Mesh dumped in %s seconds' % (time.time()-start))
|
||||
BIN
assets/blender/scripts/blender2ogre/examples/armature-test.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/axis.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/floating-bones.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/layered-materials.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/merge-group-linked-scene.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/merge-group-linked-source.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/multi-material.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/multiscene.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/multitrack-advanced.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/multitrack-simple.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/object-links.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/shape-animation.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/smooth-and-sharp-normals.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/texture.jpg
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
assets/blender/scripts/blender2ogre/examples/texture2.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
assets/blender/scripts/blender2ogre/examples/vertex-colored-alpha.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/examples/vertex-colored.blend
(Stored with Git LFS)
Normal file
BIN
assets/blender/scripts/blender2ogre/feature_tests/nodeanimation.blend
(Stored with Git LFS)
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
assets/blender/scripts/blender2ogre/images/cube-action.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
assets/blender/scripts/blender2ogre/images/dupli-offset.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
assets/blender/scripts/blender2ogre/images/extern-material.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
assets/blender/scripts/blender2ogre/images/follow-path-const.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 59 KiB |
BIN
assets/blender/scripts/blender2ogre/images/modifiers/fail.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/blender/scripts/blender2ogre/images/modifiers/ok.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/blender/scripts/blender2ogre/images/modifiers/warning.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 346 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 491 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 270 KiB |
|
After Width: | Height: | Size: 348 KiB |
BIN
assets/blender/scripts/blender2ogre/images/push-down.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 86 KiB |