mirror of https://github.com/AxioDL/metaforce.git
Updates to support VISI generation
This commit is contained in:
parent
9cf2aec5c1
commit
8c3a7da616
|
@ -11,8 +11,9 @@ bl_info = {
|
|||
# Package import
|
||||
from . import hmdl, sact, srea, swld, mapa, mapu, frme, Nodegrid, Patching
|
||||
Nodegrid = Nodegrid.Nodegrid
|
||||
import bpy, os, sys
|
||||
import bpy, os, sys, struct
|
||||
from bpy.app.handlers import persistent
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
# Appendable list allowing external addons to register additional resource types
|
||||
|
@ -69,6 +70,41 @@ def add_export_type(type_tuple):
|
|||
def command(cmdline, writepipeline, writepipebuf):
|
||||
pass
|
||||
|
||||
def mesh_aabb(writepipebuf):
|
||||
scene = bpy.context.scene
|
||||
total_min = Vector((99999.0, 99999.0, 99999.0))
|
||||
total_max = Vector((-99999.0, -99999.0, -99999.0))
|
||||
|
||||
if bpy.context.scene.hecl_type == 'ACTOR':
|
||||
sact_data = bpy.context.scene.hecl_sact_data
|
||||
for subtype in sact_data.subtypes:
|
||||
if subtype.linked_mesh in bpy.data.objects:
|
||||
mesh = bpy.data.objects[subtype.linked_mesh]
|
||||
minPt = mesh.bound_box[0]
|
||||
maxPt = mesh.bound_box[6]
|
||||
for comp in range(3):
|
||||
if minPt[comp] < total_min[comp]:
|
||||
total_min[comp] = minPt[comp]
|
||||
for comp in range(3):
|
||||
if maxPt[comp] > total_max[comp]:
|
||||
total_max[comp] = maxPt[comp]
|
||||
|
||||
elif bpy.context.scene.hecl_type == 'MESH':
|
||||
meshName = bpy.context.scene.hecl_mesh_obj
|
||||
if meshName in bpy.data.objects:
|
||||
mesh = bpy.data.objects[meshName]
|
||||
minPt = mesh.bound_box[0]
|
||||
maxPt = mesh.bound_box[6]
|
||||
for comp in range(3):
|
||||
if minPt[comp] < total_min[comp]:
|
||||
total_min[comp] = minPt[comp]
|
||||
for comp in range(3):
|
||||
if maxPt[comp] > total_max[comp]:
|
||||
total_max[comp] = maxPt[comp]
|
||||
|
||||
writepipebuf(struct.pack('fff', total_min[0], total_min[1], total_min[2]))
|
||||
writepipebuf(struct.pack('fff', total_max[0], total_max[1], total_max[2]))
|
||||
|
||||
# Load scene callback
|
||||
from bpy.app.handlers import persistent
|
||||
@persistent
|
||||
|
|
|
@ -25,6 +25,13 @@ def write_out_material(writebuf, mat, mesh_obj):
|
|||
writebuf(prop[0].encode())
|
||||
writebuf(struct.pack('i', prop[1]))
|
||||
|
||||
transparent = False
|
||||
if mat.game_settings.alpha_blend == 'ALPHA' or mat.game_settings.alpha_blend == 'ALPHA_SORT':
|
||||
transparent = True
|
||||
elif mat.game_settings.alpha_blend == 'ADD':
|
||||
transparent = True
|
||||
writebuf(struct.pack('b', int(transparent)))
|
||||
|
||||
# Takes a Blender 'Mesh' object (not the datablock)
|
||||
# and performs a one-shot conversion process to HMDL
|
||||
def cook(writebuf, mesh_obj, output_mode, max_skin_banks, max_octant_length=None):
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
from mathutils import Quaternion, Color
|
||||
import math
|
||||
from .. import Nodegrid
|
||||
|
||||
# Preview update func (for lighting preview)
|
||||
|
@ -404,6 +406,79 @@ class SREARenderLightmaps(bpy.types.Operator):
|
|||
|
||||
return {'FINISHED'}
|
||||
|
||||
def shadeless_material(idx):
|
||||
name = 'SHADELESS_MAT_%d' % idx
|
||||
if name in bpy.data.materials:
|
||||
return bpy.data.materials[name]
|
||||
mat = bpy.data.materials.new(name)
|
||||
mat.use_shadeless = True
|
||||
r = idx % 256
|
||||
g = (idx % 65536) // 256
|
||||
b = idx // 65536
|
||||
mat.diffuse_color = Color((r / 255.0, g / 255.0, b / 255.0))
|
||||
return mat
|
||||
|
||||
look_forward = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||
look_backward = Quaternion((0.0, 0.0, 1.0), math.radians(180.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||
look_up = Quaternion((1.0, 0.0, 0.0), math.radians(180.0))
|
||||
look_down = Quaternion((1.0, 0.0, 0.0), math.radians(0.0))
|
||||
look_left = Quaternion((0.0, 0.0, 1.0), math.radians(90.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||
look_right = Quaternion((0.0, 0.0, 1.0), math.radians(-90.0)) * Quaternion((1.0, 0.0, 0.0), math.radians(90.0))
|
||||
look_list = (look_forward, look_backward, look_up, look_down, look_left, look_right)
|
||||
|
||||
# Render PVS for location
|
||||
def render_pvs(pathOut, location):
|
||||
bpy.context.scene.render.resolution_x = 256
|
||||
bpy.context.scene.render.resolution_y = 256
|
||||
bpy.context.scene.render.resolution_percentage = 100
|
||||
bpy.context.scene.render.use_antialiasing = False
|
||||
bpy.context.scene.render.use_textures = False
|
||||
bpy.context.scene.render.use_shadows = False
|
||||
bpy.context.scene.render.use_sss = False
|
||||
bpy.context.scene.render.use_envmaps = False
|
||||
bpy.context.scene.render.use_raytrace = False
|
||||
bpy.context.scene.render.engine = 'BLENDER_RENDER'
|
||||
bpy.context.scene.display_settings.display_device = 'None'
|
||||
bpy.context.scene.render.image_settings.file_format = 'PNG'
|
||||
bpy.context.scene.world.horizon_color = Color((1.0, 1.0, 1.0))
|
||||
bpy.context.scene.world.zenith_color = Color((1.0, 1.0, 1.0))
|
||||
|
||||
cam = bpy.data.cameras.new('CUBIC_CAM')
|
||||
cam_obj = bpy.data.objects.new('CUBIC_CAM', cam)
|
||||
bpy.context.scene.objects.link(cam_obj)
|
||||
bpy.context.scene.camera = cam_obj
|
||||
cam.lens_unit = 'FOV'
|
||||
cam.angle = math.radians(90.0)
|
||||
|
||||
mat_idx = 0
|
||||
for obj in bpy.data.objects:
|
||||
if obj.type == 'MESH':
|
||||
if obj.name == 'CMESH':
|
||||
continue
|
||||
mat = shadeless_material(mat_idx)
|
||||
for slot in obj.material_slots:
|
||||
slot.material = mat
|
||||
mat_idx += 1
|
||||
|
||||
cam_obj.location = location
|
||||
cam_obj.rotation_mode = 'QUATERNION'
|
||||
|
||||
for i in range(6):
|
||||
cam_obj.rotation_quaternion = look_list[i]
|
||||
bpy.context.scene.render.filepath = '%s%d' % (pathOut, i)
|
||||
bpy.ops.render.render(write_still=True)
|
||||
|
||||
bpy.context.scene.camera = None
|
||||
bpy.context.scene.objects.unlink(cam_obj)
|
||||
bpy.data.objects.remove(cam_obj)
|
||||
bpy.data.cameras.remove(cam)
|
||||
|
||||
# Render PVS for light
|
||||
def render_pvs_light(pathOut, lightName):
|
||||
if lightName not in bpy.data.objects:
|
||||
raise RuntimeError('Unable to find light %s' % lightName)
|
||||
render_pvs(pathOut, bpy.data.objects[lightName].location)
|
||||
|
||||
# Cook
|
||||
def cook(writebuffunc, platform, endianchar):
|
||||
print('COOKING SREA')
|
||||
|
|
|
@ -188,6 +188,20 @@ def dataout_loop():
|
|||
if meshobj.type == 'MESH' and not meshobj.library:
|
||||
writepipestr(meshobj.name.encode())
|
||||
|
||||
elif cmdargs[0] == 'LIGHTLIST':
|
||||
lightCount = 0
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'LAMP' and not obj.library:
|
||||
lightCount += 1
|
||||
writepipebuf(struct.pack('I', lightCount))
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'LAMP' and not obj.library:
|
||||
writepipestr(obj.name.encode())
|
||||
|
||||
elif cmdargs[0] == 'MESHAABB':
|
||||
writepipestr(b'OK')
|
||||
hecl.mesh_aabb(writepipebuf)
|
||||
|
||||
elif cmdargs[0] == 'MESHCOMPILE':
|
||||
maxSkinBanks = int(cmdargs[2])
|
||||
|
||||
|
@ -281,6 +295,7 @@ def dataout_loop():
|
|||
0.0, 0.0, 0.0, 1.0))
|
||||
writepipebuf(struct.pack('fff', ambient_color[0], ambient_color[1], ambient_color[2]))
|
||||
writepipebuf(struct.pack('IIfffffb', 0, 0, ambient_energy, 0.0, 1.0, 0.0, 0.0, False))
|
||||
writepipestr(b'AMBIENT')
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
if obj.type == 'LAMP':
|
||||
|
@ -325,6 +340,8 @@ def dataout_loop():
|
|||
writepipebuf(struct.pack('IIfffffb', layer, type, obj.data.energy, spotCutoff, constant, linear, quadratic,
|
||||
castShadow))
|
||||
|
||||
writepipestr(obj.name.encode())
|
||||
|
||||
elif cmdargs[0] == 'GETTEXTURES':
|
||||
writepipestr(b'OK')
|
||||
|
||||
|
@ -386,6 +403,20 @@ def dataout_loop():
|
|||
for c in r:
|
||||
writepipebuf(struct.pack('f', c))
|
||||
|
||||
elif cmdargs[0] == 'RENDERPVS':
|
||||
pathOut = cmdargs[1]
|
||||
locX = float(cmdargs[2])
|
||||
locY = float(cmdargs[3])
|
||||
locZ = float(cmdargs[4])
|
||||
hecl.srea.render_pvs(pathOut, (locX, locY, locZ))
|
||||
writepipestr(b'OK')
|
||||
|
||||
elif cmdargs[0] == 'RENDERPVSLIGHT':
|
||||
pathOut = cmdargs[1]
|
||||
lightName = cmdargs[2]
|
||||
hecl.srea.render_pvs_light(pathOut, lightName)
|
||||
writepipestr(b'OK')
|
||||
|
||||
loaded_blend = None
|
||||
|
||||
# Main exception handling
|
||||
|
|
|
@ -85,6 +85,9 @@ static void AthenaExc(athena::error::Level level, const char* file,
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
static hecl::SystemChar cwdbuf[1024];
|
||||
hecl::SystemString ExeDir;
|
||||
|
||||
#if _WIN32
|
||||
int wmain(int argc, const wchar_t** argv)
|
||||
#else
|
||||
|
@ -138,7 +141,6 @@ int main(int argc, const char** argv)
|
|||
/* Assemble common tool pass info */
|
||||
ToolPassInfo info;
|
||||
info.pname = argv[0];
|
||||
hecl::SystemChar cwdbuf[1024];
|
||||
if (hecl::Getcwd(cwdbuf, 1024))
|
||||
{
|
||||
info.cwd = cwdbuf;
|
||||
|
@ -148,6 +150,13 @@ int main(int argc, const char** argv)
|
|||
#else
|
||||
info.cwd += _S('/');
|
||||
#endif
|
||||
|
||||
if (argv[0][0] != _S('/') && argv[0][0] != _S('\\'))
|
||||
ExeDir = hecl::SystemString(cwdbuf) + _S('/');
|
||||
hecl::SystemString Argv0(argv[0]);
|
||||
hecl::SystemString::size_type lastIdx = Argv0.find_last_of(_S("/\\"));
|
||||
if (lastIdx != hecl::SystemString::npos)
|
||||
ExeDir.insert(ExeDir.end(), Argv0.begin(), Argv0.begin() + lastIdx);
|
||||
}
|
||||
|
||||
/* Concatenate args */
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 245a39fd92da80af40dcaf5e7567ff0b4cbf9a9a
|
||||
Subproject commit 0cc794f49d8884c30ba43b195aecdfed14062a53
|
|
@ -354,6 +354,40 @@ public:
|
|||
return retval;
|
||||
}
|
||||
|
||||
std::vector<std::string> getLightList()
|
||||
{
|
||||
m_parent->_writeStr("LIGHTLIST");
|
||||
uint32_t count;
|
||||
m_parent->_readBuf(&count, 4);
|
||||
std::vector<std::string> retval;
|
||||
retval.reserve(count);
|
||||
for (uint32_t i=0 ; i<count ; ++i)
|
||||
{
|
||||
char name[128];
|
||||
m_parent->_readStr(name, 128);
|
||||
retval.push_back(name);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::pair<atVec3f, atVec3f> getMeshAABB()
|
||||
{
|
||||
if (m_parent->m_loadedType != BlendType::Mesh &&
|
||||
m_parent->m_loadedType != BlendType::Actor)
|
||||
BlenderLog.report(logvisor::Fatal, _S("%s is not a MESH or ACTOR blend"),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str());
|
||||
|
||||
m_parent->_writeStr("MESHAABB");
|
||||
char readBuf[256];
|
||||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable get AABB: %s", readBuf);
|
||||
|
||||
Vector3f min(*m_parent);
|
||||
Vector3f max(*m_parent);
|
||||
return std::make_pair(min.val, max.val);
|
||||
}
|
||||
|
||||
/* Vector types with integrated stream reading constructor */
|
||||
struct Vector2f
|
||||
{
|
||||
|
@ -418,6 +452,7 @@ public:
|
|||
std::string source;
|
||||
std::vector<ProjectPath> texs;
|
||||
std::unordered_map<std::string, int32_t> iprops;
|
||||
bool transparent;
|
||||
|
||||
Material(BlenderConnection& conn);
|
||||
bool operator==(const Material& other) const
|
||||
|
@ -675,6 +710,8 @@ public:
|
|||
float quadratic;
|
||||
bool shadow;
|
||||
|
||||
std::string name;
|
||||
|
||||
Light(BlenderConnection& conn);
|
||||
};
|
||||
|
||||
|
@ -814,6 +851,9 @@ public:
|
|||
inline const atVec3f& operator[](size_t idx) const {return m[idx];}
|
||||
};
|
||||
std::unordered_map<std::string, Matrix3f> getBoneMatrices(const std::string& name);
|
||||
|
||||
bool renderPvs(const std::string& path, const atVec3f& location);
|
||||
bool renderPvsLight(const std::string& path, const std::string& lightName);
|
||||
};
|
||||
DataStream beginData()
|
||||
{
|
||||
|
|
|
@ -264,6 +264,10 @@ static inline bool IsAbsolute(const SystemString& path)
|
|||
return false;
|
||||
}
|
||||
|
||||
const SystemChar* GetTmpDir();
|
||||
|
||||
int RunProcess(const SystemChar* path, const SystemChar* const args[]);
|
||||
|
||||
enum class FileLockType
|
||||
{
|
||||
None = 0,
|
||||
|
|
|
@ -237,16 +237,11 @@ BlenderConnection::BlenderConnection(int verbosityLevel)
|
|||
BlenderLog.report(logvisor::Info, "Establishing BlenderConnection...");
|
||||
|
||||
/* Put hecl_blendershell.py in temp dir */
|
||||
const SystemChar* TMPDIR = GetTmpDir();
|
||||
#ifdef _WIN32
|
||||
wchar_t* TMPDIR = _wgetenv(L"TEMP");
|
||||
if (!TMPDIR)
|
||||
TMPDIR = (wchar_t*)L"\\Temp";
|
||||
m_startupBlend = hecl::WideToUTF8(TMPDIR);
|
||||
#else
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
char* TMPDIR = getenv("TMPDIR");
|
||||
if (!TMPDIR)
|
||||
TMPDIR = (char*)"/tmp";
|
||||
m_startupBlend = TMPDIR;
|
||||
#endif
|
||||
|
||||
|
@ -905,6 +900,8 @@ BlenderConnection::DataStream::Mesh::Material::Material
|
|||
conn._readBuf(&val, 4);
|
||||
iprops[readStr] = val;
|
||||
}
|
||||
|
||||
conn._readBuf(&transparent, 1);
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::Mesh::Surface::Surface
|
||||
|
@ -1088,6 +1085,14 @@ BlenderConnection::DataStream::Light::Light(BlenderConnection& conn)
|
|||
: sceneXf(conn), color(conn)
|
||||
{
|
||||
conn._readBuf(&layer, 29);
|
||||
|
||||
uint32_t nameLen;
|
||||
conn._readBuf(&nameLen, 4);
|
||||
if (nameLen)
|
||||
{
|
||||
name.assign(nameLen, '\0');
|
||||
conn._readBuf(&name[0], nameLen);
|
||||
}
|
||||
}
|
||||
|
||||
BlenderConnection::DataStream::Actor::Actor(BlenderConnection& conn)
|
||||
|
@ -1662,6 +1667,51 @@ BlenderConnection::DataStream::getBoneMatrices(const std::string& name)
|
|||
|
||||
}
|
||||
|
||||
bool BlenderConnection::DataStream::renderPvs(const std::string& path, const atVec3f& location)
|
||||
{
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
if (m_parent->m_loadedType != BlendType::Area)
|
||||
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str());
|
||||
|
||||
char req[256];
|
||||
snprintf(req, 256, "RENDERPVS %s %f %f %f", path.c_str(),
|
||||
location.vec[0], location.vec[1], location.vec[2]);
|
||||
m_parent->_writeStr(req);
|
||||
|
||||
char readBuf[256];
|
||||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable to render PVS for: %s; %s",
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str(), readBuf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlenderConnection::DataStream::renderPvsLight(const std::string& path, const std::string& lightName)
|
||||
{
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
if (m_parent->m_loadedType != BlendType::Area)
|
||||
BlenderLog.report(logvisor::Fatal, _S("%s is not an AREA blend"),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str());
|
||||
|
||||
char req[256];
|
||||
snprintf(req, 256, "RENDERPVSLIGHT %s %s", path.c_str(), lightName.c_str());
|
||||
m_parent->_writeStr(req);
|
||||
|
||||
char readBuf[256];
|
||||
m_parent->_readStr(readBuf, 256);
|
||||
if (strcmp(readBuf, "OK"))
|
||||
BlenderLog.report(logvisor::Fatal, "unable to render PVS light %s for: %s; %s", lightName.c_str(),
|
||||
m_parent->m_loadedBlend.getAbsolutePath().c_str(), readBuf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlenderConnection::quitBlender()
|
||||
{
|
||||
char lineBuf[256];
|
||||
|
|
|
@ -744,4 +744,37 @@ int RecursiveMakeDir(const SystemChar* dir) {
|
|||
}
|
||||
#endif
|
||||
|
||||
const SystemChar* GetTmpDir()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
wchar_t* TMPDIR = _wgetenv(L"TEMP");
|
||||
if (!TMPDIR)
|
||||
TMPDIR = (wchar_t*)L"\\Temp";
|
||||
#else
|
||||
char* TMPDIR = getenv("TMPDIR");
|
||||
if (!TMPDIR)
|
||||
TMPDIR = (char*)"/tmp";
|
||||
#endif
|
||||
return TMPDIR;
|
||||
}
|
||||
|
||||
int RunProcess(const SystemChar* path, const SystemChar* const args[])
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
if (!pid)
|
||||
{
|
||||
execvp(path, (char * const *)args);
|
||||
exit(1);
|
||||
}
|
||||
int ret;
|
||||
if (waitpid(pid, &ret, 0) < 0)
|
||||
return -1;
|
||||
if (WIFEXITED(ret))
|
||||
return WEXITSTATUS(ret);
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue