project reality header
Go Back   Project Reality Forums > PR:BF2 Mod Forums > PR:BF2 Community Modding
01 Nov 2024, 00:00:00 (PRT)
Register Developer Blogs Members List Search Quick Links
PR:BF2 Community Modding Making or wanting help making your own asset? Check in here

Reply
 
Thread Tools Display Modes
Old 2017-08-11, 08:31   #31
[R-DEV]Mineral
PR:BF2 Lead Designer
Supporting Member
PR Server License Administrator

Mineral's Avatar
Default

General rule for destroyables is to have geom0 terrain shadows unless it's a small prop like a cone or sign or something. But those should be removed by the mapper anyway from the on scene

Mineral is offline Reply With Quote
Old 2017-08-11, 08:41   #32
Rhino
Retired PR Developer
Supporting Member

Rhino's Avatar
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

Quote:
Originally Posted by Harmonikater View Post
Consequently, custom LM resolutions are possible with the next version:
http://fh2.cmp-gaming.com/cmp_file_s...mages/BfLM.JPG
Nice, but is that for generating samples off of custom LM UVs or just for automatic LM UVs?

If it is for custom LM UVs which would be really nice, there are no options for each LOD are there?

Rhino is offline Reply With Quote
Old 2017-08-11, 11:23   #33
Heavy Death
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

And now we can get 1.5 in a matter of days!
Heavy Death is offline Reply With Quote
Old 2017-08-11, 16:35   #34
[R-CON]​Harmonikater
PR:BF2 Contributor
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

Quote:
Originally Posted by [R-DEV]Rhino View Post
Nice, but is that for generating samples off of custom LM UVs or just for automatic LM UVs?

If it is for custom LM UVs which would be really nice, there are no options for each LOD are there?


Alright we're getting closer. What I've done now is include a "User" option in the dropdown list. I assume that if one wants custom sizes for each lod, they sort of know what they're doing. Well, enough to trust them to rename lod nodes themselves.
So in case "User" selected, one can add the requested size to each lod node (including lods under possible different geoms, e.g. if we're talking destroyableObjects) such:



Can either be single values (-> square textures) or two values separated by *
This applies for both automatic and custom LM UVs.

One caveat I found is that different nodes underneath the same lod cannot differ regarding the LM UV channel, as in if one node has custom UVs, all other nodes need custom UVs as well. However I think that's a fairly rare case, considering StaticMeshes are mostly just a single node, and that somebody who makes custom UVs probably does them for all nodes anyway.
Harmonikater is offline Reply With Quote
Old 2017-08-11, 17:17   #35
Rhino
Retired PR Developer
Supporting Member

Rhino's Avatar
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

Awesome, that's perfect!

Rhino is offline Reply With Quote
Old 2017-08-11, 17:23   #36
worldlife
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

Quote:
Originally Posted by Harmonikater View Post
That's something I've wanted to include but haven't had the time yet. It wasn't super high priority for me, since skinnedmesh imports tend to be not very useful imo.
If you're going to change the actual mesh the skin modifier will likely go to hell anyway.
I guess if you want to change just the weights on some parts, that could work, but you'd still have the additional vertices where the exporter detached due to smoothing group changes.
Unless one were to manually weld verts during import while keeping the smoothing groups intact and then applying the imported skin weights based on vertex position or something like that. But I'm afraid that could become fairly complex and resource-intensive pretty quickly.
Of course it would also benefit normal non-weighted meshes in the end as well.
But in my experience just letting max weld and autosmooth leads to too many errors.
Think of a small cylinder whose ends the player doesn't see, there's no problem in making it 4-sided with one smoothing group around. That create obvious problems for autosmooth.
About welding vertices, I planned to group all vertices that have the same position together before creating the mesh and hence creating a mesh that have no duplicated vertices. But yeah that's a lot of work -- UV vertices must be considered too, and I'm a little confused about uv vertices in 3dsmax.

Quote:
Then again I'm doing everything in maxscript, if this were done in SDK the entire import would probably a hell of a lot quicker. Unfortunately I have no experience with max SDK, but maybe you do? (You didn't mention how you implemented your importer)
I implement it in maxscript as well (I hate c++).

Quote:
In any case, would you mind sharing your code here?
Sure. The code's a bit long though. Uncomment the last line to have a test.
Code:
-- bf2mesh.ms

g_bf2doInclude "animation/skeImport.ms"

-- helper functions
fn BF2ReadString fp=(
	local length = ReadLong fp #unsigned
	result = ""
	for i=1 to length do(
		local char = bit.intaschar (ReadByte fp #unsigned)
		append result char
	)
	return result
	--return ReadString fp
)

fn BF2AddVertsToChannel obj mapChannel vertsList=(
	if meshop.getMapSupport obj mapChannel != true then
		meshop.setMapSupport obj mapChannel true
	if obj.numverts != vertsList.count then format "ERROR in BF2AddVertsToChannel: numverts!=numtverts"
	for i=1 to vertsList.count do
	(
		meshop.setMapVert obj mapChannel i vertsList[i]
	)
)

fn BF2CreateBoneFromFile fname boneObjs s=(

		local f = fopen fname "rb"
		if (f != undefined) then
		(
			if s == undefined then s = 1.0 / BF2_GetUnitMultiplier()
			
			local version = readlong f
			format "version: %\n" version
			
			local numBones = readlong f
			
			local parentIDs = #()

			local rightHandID = -2

			for i=1 to numBones do
			(
				local tmpName = bf2ske.readName f
				
				
				local tmpNameLC = lowercase tmpName
				

				
				if tmpName == "right_ullna" then rightHandID = i - 1
				if tmpName == "torus" then rightHandID = i - 1
					
				local motherInx = readShort f
				
				local tmpQuat = #()
				for j=1 to 4 do append tmpQuat (readFloat f)
				
				local tmpPos = [0,0,0]
				tmpPos.x = (readFloat f) * s
				tmpPos.z = (readFloat f) * s
				tmpPos.y = (readFloat f) * s
				
				
				local motherObj = undefined
				if motherInx != -1 then
				(
				--	format " %  %\n" motherInx boneObjs[motherInx+1]
					if isValidNode boneObjs[motherInx+1] then
					(
						motherObj = boneObjs[motherInx+1]
						parentIDs[i] = motherInx + 1
					)
				)
			
				
				if (partialCompareStr tmpName "mesh") then
				(
					if rightHandID == motherInx then
					(
						local newBone = Box name:tmpName length:(0.25) width:(0.25) height:(0.25)
						newBone.parent = boneObjs[motherInx+1]
						newBone.transform = newBone.parent.transform
						
						continue
					)
				)
		
				local newBone = bf2ske.makeBone tmpName tmpQuat tmpPos motherObj
				boneObjs[i] = newBone
			
				
				format "%_% (%) \t% %\n" i tmpName (motherInx+1) tmpQuat tmpPos
			)
			fclose f

			local boneSz = 0.15		
			
			local rtNode = Dummy name:("root_skeleton_" + (getFilenamefile fname)) size:(0.4*s)
			for i=1 to numBones do
			(
				if isValidNode boneObjs[i] then
				(
					if parentIDs[i] != undefined then
					(	
					--	boneObjs[i].parent = boneObjs[parentIDs[i]]
					)
					else
					(
						boneObjs[i].parent = rtNode 
					)
				)
			)
			return rtNode
		)
		return false
)


-- bounding box
-- 24 bytes
struct aabb
(
	bbmin, --Point3
	bbmax, --Point3
	-- functions
	fn Read fp=(
		local vx = (ReadFloat fp)
		local vy = (ReadFloat fp)
		local vz = (ReadFloat fp)
		bbmin = [vx,vz,vy]
		local vx = (ReadFloat fp)
		local vy = (ReadFloat fp)
		local vz = (ReadFloat fp)
		bbmax = [vx,vz,vy]
	)
)

-- 4x4 transformation matrix
struct matrix4 -- 64 bytes
(
	m, --BigMatrix 4*4
	-- functions
	fn Read fp=(
		m = BigMatrix 4 4
		for i=1 to 4 do (
			for j=1 to 4 do m[i][j]=(ReadFloat fp)
		)	
	),
	fn convertTransform s inverseTrans:false=(
		if m==undefined then return m
		else(
			local mat = matrix3 [m[1][1],m[1][3],m[1][2]] [m[3][1],m[3][3],m[3][2]] [m[2][1],m[2][3],m[2][2]] ([m[4][1],m[4][3],m[4][2]]*s)
			if inverseTrans then return mat
			else return (inverse mat)
		)	
	)
)

--bf2 vertex structure
struct bf2vertex
(
	position,		--0
	blendweight,	--1
	blendindices,	--2
	normal,			--3
	
	texcoord,		--5
	tangent,		--6
	
	uv2,			--261
	uv3,			--517
	uv4,			--773
	uv5,			--1029
	
	--fnList includes all functions the Read function should use
	fn Read fp version attribList=(
		for i=1 to attribList.count do(
			--check flag
			if attribList[i].flag != 0 then continue
			
			--read data
			local ret = case attribList[i].vartype of(
				--D3DDECLTYPE_FLOAT1
				0: ReadFloat fp
				--D3DDECLTYPE_FLOAT2
				1: [(ReadFloat fp),1-(ReadFloat fp),0] --this is used as UV, V is inverted, and we insert a 0 as W
				--D3DDECLTYPE_FLOAT3
				2: [(ReadFloat fp),(ReadFloat fp),(ReadFloat fp)]
				--D3DDECLTYPE_D3DCOLOR(4 ubytes)
				4: #((ReadByte fp #unsigned),(ReadByte fp #unsigned),(ReadByte fp #unsigned),(ReadByte fp #unsigned))
				
				default: format "vartype % not supported" attribList[i].vartype
			)
			if ret==undefined then continue
			
			--store data
			case attribList[i].usage of(
				--D3DDECLUSAGE_POSITION
				0: position = [ret.x, ret.z, ret.y]--ret
				--D3DDECLUSAGE_BLENDWEIGHT
				1: blendweight = ret
				--D3DDECLUSAGE_BLENDINDICES
				2: blendindices = ret
				--D3DDECLUSAGE_NORMAL
				3: normal = ret
				--D3DDECLUSAGE_TEXCOORD
				5: texcoord = ret
				--D3DDECLUSAGE_TANGENT
				6: tangent = ret
				--uv2
				261: uv2 = ret
				--uv3
				517: uv3 = ret
				--uv4
				773: uv4 = ret
				--uv5
				1029: uv5 = ret
				
				default: format "usage % not supported" attribList[i].usage			
			)
		)
	)
)

-- bf2 mesh file header
struct bf2head             -- 20 bytes
(
	u1,          -- (uint)0
	version,     -- (uint)10 for most bundledmesh, 6 for some bundledmesh, 11 for staticmesh
	u3,          -- (uint)0
	u4,          -- (uint)0
	u5,          -- (uint)0
	-- functions
	fn Read fp=(
		u1 = ReadLong fp #unsigned
		version = ReadLong fp #unsigned
		u3 = ReadLong fp #unsigned
		u4 = ReadLong fp #unsigned
		u5 = ReadLong fp #unsigned
	)
)


-- vertex attribute table entry
struct bf2attrib           -- 8 bytes
(
	flag,      -- (ushort)some sort of boolean flag (if true the below field are to be ignored?)
	offset,    -- (ushort)offset from vertex data start
	vartype,   -- (ushort)attribute type (vec2, vec3 etc)
	usage,     -- (ushort)usage ID (vertex, texcoord etc)
	-- functions
	fn Read fp=(
		flag = ReadShort fp #unsigned
		offset = ReadShort fp #unsigned
		vartype = ReadShort fp #unsigned
		usage = ReadShort fp #unsigned
	)
 -- Note: "usage" field correspond to the definition in DX SDK "Include\d3d9types.h"
 --       Looks like DICE extended these for additional UV channels, these constants
 --       are much larger to avoid conflict with core DX enums.
)


-- bone structure
struct bf2bone       -- 68 bytes
(
	id,    -- (uint)4 bytes
	transform,  -- (matrix4)64 bytes
	fn Read fp version=(
		id = ReadLong fp #unsigned
		transform = matrix4()
		transform.Read fp
	)
)


-- rig structure
struct bf2rig
(
	bonenum,	-- (int)
	bone,  		-- #(bf2bone)

	-- constructor/destrutor

	-- functions
	fn Read fp version= (
		bonenum = ReadLong fp #signed
		format "   bonenum: %\n" bonenum
		bone = #()
		for i = 1 to bonenum do(
			local tmpbone = bf2bone()
			tmpbone.Read fp version
			append bone tmpbone
			format "   boneid[%]: %\n" i tmpbone.id
		)
	)
)


-- material (aka drawcall)
struct bf2mat
(
	alphamode,             -- (uint)0=opaque, 1=blend, 2=alphatest
	fxfile,                -- (string)shader filename string
	technique,             -- (string)technique name

	-- texture map filenames
	mapnum,                -- (uint)number of texture map filenames
	map,                  -- #(string)map filename array

	-- geometry info
	vstart,                -- (uint)vertex start offset
	istart,                -- (uint)index start offset
	inum,                  -- (uint)number of indices
	vnum,                  -- (uint)number of vertices

	-- misc
	u4,                    -- (uint)always 1?
	u5,                  -- (ushort)always 0x34E9?
	u6,                  -- (ushort)most often 18/19
	bounds,                       -- (aabb)per-material bounding box (StaticMesh only)

	-- constructor/destructor

	-- functions
	fn Read fp version isSkn=(
		-- alpha flag (4 bytes)
		if not isSkn do (
			alphamode = ReadLong fp #unsigned
			format "   alphamode: %\n" alphamode
		)
		
		-- fx filename
		fxfile = BF2ReadString fp
		format "   fxfile: %\n" fxfile
		
		-- material name
		technique = BF2ReadString fp
		format "   matname: %\n" technique
		
		-- mapnum (4 bytes)
		mapnum = ReadLong fp #unsigned
		format "   mapnum: %\n" mapnum
		map = #()
		for i= 1 to mapnum do(
			tmpstr = (BF2ReadString fp)
			append map tmpstr
			format "    map %: %\n" i tmpstr
		)
		
		--geometry info
		vstart = ReadLong fp #unsigned
		istart = ReadLong fp #unsigned
		inum = ReadLong fp #unsigned
		vnum = ReadLong fp #unsigned
		format "	vstart: %\n" vstart
		format "	istart: %\n" istart
		format "	inum: %\n" inum
		format "	vnum: %\n" vnum
		
		--unknown
		u4 = ReadLong fp #unsigned
		u5 = ReadShort fp #unsigned
		u6 = ReadShort fp #unsigned
		
		--bounds
		if not isSkn do (
			if version == 11 do(
				bounds = aabb()
				bounds.Read fp
			)
		)
		
		return true
	)
)


-- lod, holds mainly a collection of materials
struct bf2lod
(
	-- bounding box
	bbmin,	-- (Point3)
	bbmax,	-- (Point3)
	pivot,	-- (Point3)not sure this is really a pivot (only on version<=6)

 	-- skinning matrices (SkinnedMesh only)
 	rignum,              -- (int)this usually corresponds to meshnum (but what was meshnum again??)
 	rig,             -- #(bf2rig)array of rigs

	-- nodes (staticmesh and bundledmesh only)
	nodenum,	--(int)
	node,		--#(matrix4)

	-- material/drawcalls
	matnum,              -- (int)number of materials
	mat,             -- #(bf2mat)material array

	-- constructor/destructor

	-- functions
	fn ReadNodeData fp version isSkn isBn isBFP4F=(--return bool
		--bounds (24 bytes)
		vx = (ReadFloat fp)
		vy = (ReadFloat fp)
		vz = (ReadFloat fp)
		bbmin = [vx,vz,vy]
		vx = (ReadFloat fp)
		vy = (ReadFloat fp)
		vz = (ReadFloat fp)
		bbmax = [vx,vz,vy]
		
		--unknown (12 bytes)
		if version <= 6 do( --version 4 and 6
			vx = (ReadFloat fp)
			vy = (ReadFloat fp)
			vz = (ReadFloat fp)
			pivot = [vx,vz,vy]
		)
		
		--skinnedmesh has different rigs
		if isSkn then(
			rignum = ReadLong fp #signed
			format "  rignum: %\n" rignum
			rig = #()
			for i=1 to rignum do(
				format "  rig block % start at %\n" i (ftell fp)
				local tmprig = bf2rig()
				tmprig.Read fp version
				append rig tmprig
				--format "	bone: %\n" tmprig.bone
				format "  rig block % end at %\n" i (ftell fp)
			)
		)
		else(
			nodenum = ReadLong fp #signed
			format "  nodenum: %\n" nodenum
			if not isBn do(
				format "   node data\n"
				node = #()
				for i=1 to nodenum do(			
					local tmpnode = matrix4()
					tmpnode.Read fp
					format "	node transform: %\n" tmpnode
					append node tmpnode
				)
			)	
			--node matrices (BFP4F variant)
            if isBn and isBFP4F then(
				format "   node data\n"
				node = #()
				for i=1 to nodenum do(
					local tmpnode = matrix4()
					tmpnode.Read fp
					format "	node transform: %\n" tmpnode
					append node tmpnode
					local tmpname = BF2ReadString fp --just discard it..
					format "node matrix string: %\n" tmpname
				)
			)
		)
		
		return true	
	),
	
	fn ReadMatData fp version isSkn=(--return bool
		matnum = ReadLong fp #signed
		format "  matnum: %\n" matnum
		mat = #()
		for i=1 to matnum do(
			format "  mat block % start at %\n" i (ftell fp)
			local tmpmat = bf2mat()
			if not tmpmat.Read fp version isSkn then return false
			append mat tmpmat
			format "  mat block % end at %\n" i (ftell fp)
		)
		return true
	)
)


-- geom, holds a collection of LODs
struct bf2geom
(
	lodnum,          -- (int)number of LODs
	lod,             -- #(bf2lod)array of LODs

	-- constructor/destructor

	-- functions
	fn Read fp version= ( -- return bool
		lodnum = ReadLong fp #signed
		format "  lodnum: %\n" lodnum
		lod = #()
		for i=1 to lodnum do(
			local tmplod = bf2lod()
			append lod tmplod
		)
	)
)


-- BF2 mesh file structure
struct bf2mesh
(
	-- header
	head,			--(bf2head)

	-- unknown
	u1,				-- (ubyte)always 0?

	-- geoms
	geomnum,                 -- (int)numer of geoms
	geom,                    -- #(bf2geom)geom array

	-- vertex attribute table
	vertattribnum,               -- (int)number of vertex attribute table entries
	vertattrib,            -- #(bf2attrib)array of vertex attribute table entries

	-- vertices
	vertformat,                   -- (int)always 4?  (e.g. GL_FLOAT)
	vertstride,                   -- (int)vertex stride
	vertnum,                      -- (int)number of vertices in buffer
	vert,                      -- #(bf2vertex)vertex array

	-- indices
	indexnum,                     -- (int)number of indices
	index,           			 -- #(ushort)index array

	-- unknown
	u2,                          -- (int)always 8?

	-- constructor/destructor

	-- internal/hacks
	isSkinnedMesh=false,	--bool
	isBundledMesh=false,	--bool
	isBFP4F=false,		--bool
	-- functions
	fn Load filename= (
		local fp = fopen filename "rb"
		if fp == undefined then(
			format "File \"%\" not found.\n" filename
			return 1
		)
		
		--header
		head = bf2head()
		format "head start at %\n" (ftell fp)
		head.Read fp
		format " version: %\n"  head.version
		format "head end at %\n" (ftell fp)
		format "\n"
		
		--unknown (1 byte)
		u1 = ReadByte fp #unsigned
		
		--for BFP4F, the value is "1", so perhaps this is a version number as well
		if u1 == 1 do isBFP4F = true
		
		--- geom table ---------------------------------------------------------------------------
		format "geom table start at %\n" (ftell fp)
		--geomnum (4 bytes)
		geomnum = ReadLong fp #signed
		format " geomnum: %\n" geomnum
		geom = #()
		for i=1 to geomnum do(
			tmpgeom = bf2geom()
			tmpgeom.Read fp head.version
			append geom tmpgeom
		)
		format "geom table end at %\n" (ftell fp) 
		format "\n"
		
		--- vertex attribute table -------------------------------------------------------------------------------
		format "attrib block at %\n" (ftell fp)
		--vertattribnum (4 bytes)
		vertattribnum = ReadLong fp #signed
		format "vertattribnum: %\n" vertattribnum
		vertattrib = #()
		for i=1 to vertattribnum do(
			tmpvertattrib = bf2attrib()
			tmpvertattrib.Read fp
			append vertattrib tmpvertattrib
			format " attrib[%]: % % % %\n" i tmpvertattrib.flag \
                                          tmpvertattrib.offset \
                                          tmpvertattrib.vartype \
                                          tmpvertattrib.usage
		)
		format "attrib block end at %\n" (ftell fp)
		format "\n"
		
		--- vertices -----------------------------------------------------------------------------
		format "vertex block start at %\n" (ftell fp)
		vertformat = ReadLong fp #signed
		vertstride = ReadLong fp #signed
		vertnum = ReadLong fp #signed
		format " vertformat: %\n" vertformat
		format " vertstride: %\n" vertstride
		format " vertnum: %\n" vertnum
		if vertformat!=4 then (
			format "vertformat not supported!" 
			return 1
		)
		vert = #()
		for i=1 to vertnum do(
			tmpvert = bf2vertex()
			tmpvert.Read fp version vertattrib
			append vert tmpvert
		)		
		format "vertex block end at %\n" (ftell fp)
		
		--- indices ------------------------------------------------------------------------------
		format "index block start at %\n" (ftell fp)
		indexnum = ReadLong fp #signed
		format " indexnum: %\n" indexnum
		index = #()
		for i=1 to indexnum do(
			append index (ReadShort fp #unsigned)
		)		
		
		format "index block end at %\n" (ftell fp)
		format "\n"
		--- rigs -------------------------------------------------------------------------------
		--unknown (4 bytes)
		if not isSkinnedMesh then(
			u2 = ReadLong fp #signed		
		)
		--rigs/nodes
		format "nodes chunk start at %\n" (ftell fp)
		for i=1 to geomnum do(
			format " geom % start\n" (i-1)
			for j=1 to geom[i].lodnum do(
				format "  lod % start\n" (j-1)
				geom[i].lod[j].ReadNodeData fp head.version isSkinnedMesh isBundledMesh isBFP4F
				format "  lod % end\n" (j-1)
			)
			format " geom % end\n" (i-1)
		)		
		format "nodes chunk end at %\n" (ftell fp)
		format "\n"
		--- geoms ------------------------------------------------------------------------------
		format "geoms chunk start at %\n" (ftell fp)
		for i=1 to geomnum do(
			format " geom % start\n" (i-1)
			for j=1 to geom[i].lodnum do(
				format "  lod % start\n" (j-1)
				geom[i].lod[j].ReadMatData fp head.version isSkinnedMesh
				format "  lod % end\n" (j-1)
			)
			format " geom % end\n" (i-1)
		)		
		format "geoms chunk end at %\n" (ftell fp)
		format "\n"

		--end of file
		format "done reading %\n" (ftell fp)
		fseek fp 0 #seek_end
		format "file size is %\n" (ftell fp)
		format "\n"
		
		--close file
		fclose fp
		
		--success
		return 0
	),
	
	fn createInstance forLightmap s=(
		--add map search path
		mapPaths.add (bf2GetSetting "outModPath")
		if head == undefined then return() --not Loaded
		format "%" Point() --help latter objects successfully create
		local rootDummy = Point()
		rootDummy.name = "root"
		if isSkinnedMesh then append rootDummy.name "_skinnedmesh"
		else if isBundledMesh then append rootDummy.name "_bundledmesh"
		else append rootDummy.name "_staticmesh"	
		for i=1 to geomnum do(
			format " geom % start\n" (i-1)
			local geomDummy = Dummy name:("geom"+((i-1) as string)) position:[0,0,0]
			append rootDummy.children geomDummy
			--preload skeleton for skinnedmesh
			local skeRoot = undefined
			local boneObjs = #()
			if isSkinnedMesh then(
				--import skeleton
				local skeName = getOpenFileName caption:("Import .ske for geom"+((i-1) as string)) types:"Skeleton .ske|*.ske|All|*.*|"
				-- If the user did not cancel the file open dialog
				if skeName != undefined then(
					skeRoot = BF2CreateBoneFromFile skeName boneObjs s	
					--adjust bone positions
					local boneTransforms = #()
					local tmpIDs = #()
					for j=1 to geom[i].lodnum do(					
						for k=1 to geom[i].lod[j].rignum do(
							local tmprig = geom[i].lod[j].rig[k]
							for l=1 to tmprig.bonenum do(
								local tmpbone = tmprig.bone[l]
								if boneTransforms[tmpbone.id+1] == undefined then(
									--local boneObj = boneObjs[tmpbone.id+1]
									append tmpIDs (tmpbone.id+1)
									boneTransforms[tmpbone.id+1] = tmpbone.transform.convertTransform s
								)
							)
						)
					)
					
					--maxid = amax tmpIDs --start from 0
					--for j=1 to (maxid+1) do(
					/*for j in tmpIDs do(
						--if (findItem tmpIDs j) != 0 then(
							local boneObj = boneObjs[j]
							if boneTransforms[j]!=undefined then (								
								if boneObj.parent == skeRoot then (
									boneObj.transform = boneTransforms[j]
								)
								else(
									--get parent transform 
									local parentIndex = findItem boneObjs boneObj.parent
									if boneTransforms[parentIndex] != undefined then
										boneObj.transform = (inverse boneObj.parent.transform)*boneTransforms[j]--boneTransforms[parentIndex])
										--boneObj.transform = inverse ((inverse boneTransforms[j])*boneObj.parent.transform)
									else format "ERROR! bone % parent not found in rig transforms!\n" (j-1)
								)
							)
						--)					
					)*/
					
					--renew transform from top to bottom
					fn renewTrans nodes boneObjs boneTransforms=(
						while nodes.count > 0 do(
							local childrens = #()
							for node in nodes do(
								local nodeID = findItem boneObjs node
								if classof node == Pyramid and nodeID>0 and boneTransforms[nodeID]!=undefined then node.transform = boneTransforms[nodeID]
								join childrens node.children
							)
							nodes = childrens
						) 
					)
					renewTrans skeRoot.children boneObjs boneTransforms
				)
				else(
					skeRoot = Dummy name:"root_skeleton_debug"
					--create debug bones
					local tmpChildren = #()
					local tmpIDs = #()
					for j=1 to geom[i].lodnum do(					
						for k=1 to geom[i].lod[j].rignum do(
							local tmprig = geom[i].lod[j].rig[k]
							for l=1 to tmprig.bonenum do(
								local tmpbone = tmprig.bone[l]
								if boneObjs[tmpbone.id+1] == undefined then(
									local bonename = (tmpbone.id as string)
									if bonename.count==1 then bonename = "0"+bonename --bone id cannot exceed 99
									local boneObj = Pyramid name:bonename transform:(tmpbone.transform.convertTransform s) width:(0.01*s) depth:(0.01*s) height:(0.05*s)
									append tmpChildren boneObj
									append tmpIDs tmpbone.id
									boneObjs[tmpbone.id+1] = boneObj
								)
							)
						)
					)
					--add missed id bones
					maxid = amax tmpIDs
					for j=0 to maxid do( --id start from 0
						if (findItem tmpIDs j) == 0 then(
							local bonename = (j as string)
							if bonename.count==1 then bonename = "0"+bonename
							local boneObj = Pyramid name:bonename width:(0.03*s) depth:(0.03*s) height:(0.1*s)
							append tmpChildren boneObj
							boneObjs[j+1] = boneObj
						)
					)
					--sort names
					fn sortByName obj1 obj2=(
						if obj1.name>obj2.name then return 1
						else return -1
					)
					qsort tmpChildren sortByName
					format "	debug bones: %\n" tmpChildren
					for j=1 to tmpChildren.count do(
						append skeRoot.children tmpChildren[j]
					)
				)
				append geomDummy.children skeRoot
				--adjust bone length
				for b in boneObjs do
				(
					if classof b == Pyramid then
					(	
						if b.children.count == 1 then
						(
							local distFromObjs = distance b b.children[1]
							local dirPointing = pointingSameDir b.transform b.children[1].pos
							if dirPointing == 1 then
							(
								b.height = distFromObjs
							)
							else
							(
								if dirPointing == -1 then
								(
									b.height = -distFromObjs
								)
								else
								(
										-- not pointing directly at only child!
								)
							)
						)
					)
				)
				
			)
			for j=1 to geom[i].lodnum do(
				format "  lod % start\n" (j-1)
				local lodDummy = Dummy name:("lod"+((j-1) as string)) position:[0,0,0]
				append geomDummy.children lodDummy
				if isSkinnedMesh then(
					--use rig info of geom[i].lod[j]
					--check bone info
					if skeRoot.name != "root_skeleton_debug" then(
						--TODO: check bone number
						
					)
					--local mat = MultiSubMaterial numsubs:geom[i].lod[j].matnum name:"BF2Object"
					-- read vert and face from bf2mat
					for k=1 to geom[i].lod[j].matnum do(
						local nodesInfo=#(#(),#(),#(),#(),#(),#()) -- #(#(verts),#(faces),#(blendweight),#(uvws),#(normals),#(boneIDs#(x,y)))
						local tmpmat = geom[i].lod[j].mat[k]
						--material(TODO: use bf2 bundledmesh material)
						local stdmat = StandardMaterial name:"BF2Object"
						stdmat.showInViewport = true
						--mat.MaterialList[k] = stdmat
						--mat.MaterialList[k].name = tmpname
						for l=1 to tmpmat.mapnum do( 
							if (lowercase tmpmat.map[l]) == "common\\textures\\specularlut_pow36.dds" then continue -- do not include Specular_LUT
							local tmpTex = BitmapTexture()
							tmpTex.filename = tmpmat.map[l]
							--stdmat.maps[l+1] = tmpTex
							if classof stdmat.diffuseMap != BitmapTexture then(
								stdmat.diffuseMap = tmpTex
							)
							else if classof stdmat.BumpMap != BitmapTexture then(
								stdmat.BumpMap = tmpTex
							)
							else stdmat.SelfIllumMap = tmpTex
						)
						-- vertices/uv/normal/blendweight/boneid
						for l=(tmpmat.vstart+1) to (tmpmat.vstart+tmpmat.vnum) do(
							--local nodeIndex = vert[l].blendindices[1]+1
							append nodesInfo[1] (vert[l].position*s) --scale
							append nodesInfo[3] vert[l].blendweight
							append nodesInfo[4] vert[l].texcoord
							append nodesInfo[5] vert[l].normal
							append nodesInfo[6] #(vert[l].blendindices[1]+1, vert[l].blendindices[2]+1)
						)
						-- faces
						local tmpindex = #()
						for l=(tmpmat.istart+1) to (tmpmat.istart+tmpmat.inum) do(
							--format "index: %	face: %\n" index[l] (index[l]-tmpmat.vstart)
							append tmpindex (index[l]+1)
							if tmpindex.count==3 then( --triangle
								-- faces
								append nodesInfo[2] [tmpindex[3],tmpindex[2],tmpindex[1]] -- inverted
								tmpindex = #()
							)
						)
						
						format "	vertices: %\n	faces: %\n	blendweights: %\n	UVs: %\n	normals: %\n	boneIDs: %\n" nodesInfo[1] nodesInfo[2] nodesInfo[3] nodesInfo[4] nodesInfo[5] nodesInfo[6]
						local obj = mesh vertices:nodesInfo[1] faces:nodesInfo[2] tverts:nodesInfo[4] 
						
						obj.wirecolor = (random [0,0,0] [1,1,1]) as color
						obj.name = (k-1) as string
						if obj.name.count<2 then obj.name = "0"+obj.name
						
						-- normals
						for l=1 to obj.numverts do(
							setNormal obj l nodesInfo[5][l]
						)
						
						-- uv mapping
						--format "% %" obj.numverts obj.numtverts
						buildTVFaces obj
						for l=1 to obj.numfaces do(
							setTVFace obj l (getFace obj l)
						)
						
						--skin modifier
						addModifier obj (Skin())
						local skinMod = obj.modifiers[#skin]
						-- necessary for skinOps to work
						max modify mode
						select obj
						skinMod.bone_Limit = 2 -- only 2 bones affect one vert currently in game
						local tmprig = geom[i].lod[j].rig[k]
						--add bones
						for l=1 to tmprig.bonenum do(
							local updateInt = 0
							if l==tmprig.bonenum then updateInt=-1 --update modifier at the last add
							skinOps.addbone skinMod boneObjs[tmprig.bone[l].id+1] updateInt
						)
						--add weight
						for l=1 to obj.numverts do(
							local boneInfo = nodesInfo[6][l]
							local weightInfo
							if boneInfo[1]==boneInfo[2] then(
								boneInfo = boneInfo[1]
								weightInfo = 1.0
							)
							else weightInfo = #(nodesInfo[3][l],1-nodesInfo[3][l])
							skinOps.SetVertexWeights skinMod l boneInfo weightInfo
						)
						
						-- end
						append lodDummy.children obj
						obj.material = stdmat
					)
				)
				else(
					--use node info
					if isBundledMesh then(
						--use no-transform node
						local nodesInfo=#(#(),#(),#(),#(),#(),#(),#(),#(),#()) -- #(#(verts),#(faces),#(matIDs),#(uvws),#(nodeIndex(for face)),#(normals),#(blendindices[1]),#(uv2),#(blendindices[4](uvtranslate)))
						local indoffset = 0
						local multimat = MultiSubMaterial numsubs:geom[i].lod[j].matnum name:"BF2Object"
						-- read vert and face from bf2mat
						for k=1 to geom[i].lod[j].matnum do(
							local tmpmat = geom[i].lod[j].mat[k] 
							--material(TODO: use bf2 bundledmesh material)
							local tmpname = tmpmat.technique
							local isAnimatedUV = ((subString tmpname 1 10)=="AnimatedUV")
							/*if tmpmat.alphamode!=0 then(
								append tmpname ("|| alphamode=" + (tmpmat.alphamode as string))
							)*/
							local stdmat = StandardMaterial name:tmpname
							stdmat.showInViewport = true
							multimat.MaterialList[k] = stdmat
							--mat.MaterialList[k].name = tmpname
							for l=1 to tmpmat.mapnum do( 
								if (lowercase tmpmat.map[l]) == "common\\textures\\specularlut_pow36.dds" then continue -- do not include Specular_LUT
								local tmpTex = BitmapTexture()
								tmpTex.filename = tmpmat.map[l]
								--stdmat.maps[l+1] = tmpTex
								if classof stdmat.diffuseMap != BitmapTexture then(
									stdmat.diffuseMap = tmpTex
								)
								else if classof stdmat.BumpMap != BitmapTexture then(
									stdmat.BumpMap = tmpTex
								)
								else stdmat.SelfIllumMap = tmpTex
							)
							local aspectRatio
							if isAnimatedUV and stdmat.diffuseMap!=undefined then(
								local tmpbitmap = stdmat.diffuseMap.bitmap
								if stdmat.diffuseMap.bitmap!=undefined then aspectRatio = (tmpbitmap.height as Float)/tmpbitmap.width
								else messagebox ("Texture" + stdmat.diffuseMap.filename + "Not Found! Make sure you put the texture in the mod output path!")
								format "bitmap % aspectRatio = %" stdmat.diffuseMap.filename aspectRatio
							)
							-- vertices/uv/normal
							for l=(tmpmat.vstart+1) to (tmpmat.vstart+tmpmat.vnum) do(
								--local nodeIndex = vert[l].blendindices[1]+1
								append nodesInfo[1] (vert[l].position*s) --scale
								--append nodesInfo[4] vert[l].texcoord
								append nodesInfo[6] vert[l].normal
								append nodesInfo[7] (vert[l].blendindices[1]+1)
								append nodesInfo[9] vert[l].blendindices[4]
								--if vert[l].uv2 != undefined then append nodesInfo[8] vert[l].uv2
								--uvs
								if vert[l].uv2 != undefined then (
									
									if isAnimatedUV then(
										if vert[l].blendindices[4]==1 or vert[l].blendindices[4]==3 then( --rotate uv
											--format "Rotate UV vert %\n" l
											append nodesInfo[4] (vert[l].texcoord+[vert[l].uv2.x*aspectRatio,1-vert[l].uv2.y,0])
											append nodesInfo[8] vert[l].texcoord
										)
										else(
											append nodesInfo[4] vert[l].texcoord
											append nodesInfo[8] vert[l].texcoord
										)
									)
									else(
										append nodesInfo[4] vert[l].texcoord
										append nodesInfo[8] vert[l].uv2
									)
								)
								else (
									append nodesInfo[4] vert[l].texcoord
									--append nodesInfo[8] vert[l].texcoord
								)
							)
							-- faces
							local tmpindex = #()
							for l=(tmpmat.istart+1) to (tmpmat.istart+tmpmat.inum) do(
								--format "index: %	face: %\n" index[l] (index[l]-tmpmat.vstart)
								append tmpindex (index[l]+1)
								if tmpindex.count==3 then( --triangle
									--nodeIndex
									local nodeIndex = vert[tmpmat.vstart+tmpindex[1]].blendindices[1]+1
									--check if all 3 vertices belongs to the same node
									local isBlendFace = false
									for m=2 to 3 do(
										if nodeIndex != vert[tmpmat.vstart+tmpindex[m]].blendindices[1]+1 then(
											--format "A Blend Face: %\n" tmpindex 
											isBlendFace = true
										)										
									)
									if not isBlendFace then(
										append nodesInfo[5] nodeIndex
										-- faces
										append nodesInfo[2] ([tmpindex[3],tmpindex[2],tmpindex[1]]+indoffset) -- inverted
										-- matIDs
										append nodesInfo[3] k
									)
									else(
										append nodesInfo[5] 0
										-- faces
										append nodesInfo[2] ([tmpindex[3],tmpindex[2],tmpindex[1]]+indoffset) -- inverted
										-- matIDs
										append nodesInfo[3] k
									)
									tmpindex = #()
								)
							)
							indoffset = indoffset + tmpmat.vnum
						)
						
						-- create node instance
						--for k=1 to geom[i].lod[j].nodenum do(
						format "	vertices: %\n	faces: %\n	matIDs: %\n	UVs: %\n	nodeIndices: %\n	normals: %\n" nodesInfo[1] nodesInfo[2] nodesInfo[3] nodesInfo[4] nodesInfo[5] nodesInfo[6]
						format "	vertices count: %\n	faces count: %\n	tverts count: %\n" nodesInfo[1].count nodesInfo[2].count nodesInfo[4].count
						local obj = mesh vertices:nodesInfo[1] faces:nodesInfo[2] materialIDS:nodesInfo[3] tverts:nodesInfo[4]
						--obj.name = (k-1) as string
						-- normals
						for k=1 to obj.numverts do(
							setNormal obj k nodesInfo[6][k]
						)
												
						-- uv mapping
						--format "% %" obj.numverts obj.numtverts
						buildTVFaces obj
						for k=1 to obj.numfaces do(
							setTVFace obj k (getFace obj k)
						)
						
						--add vertex color according to blendindices[4]
						/*
						key: [] index value

						Right Side
						- treads .50 r [5]
						- rotate .71 ( r and b ) [3]
						- translate .60 [4]


						Left Side
						- treads .40 r [6]
						- rotate .91  ( r and b ) [1]
						- translate .80 [2]
						*/
						local vertColors = #()
						for k=1 to nodesInfo[9].count do(
							case nodesInfo[9][k] of(
								1: append vertColors [0.91,0,0]
								2: append vertColors [0.80,0,0]
								3: append vertColors [0.71,0,0]
								4: append vertColors [0.60,0,0]
								5: append vertColors [0.50,0,0]
								6: append vertColors [0.40,0,0]
								default: append vertColors [0,0,0]
							)
						)
						BF2AddVertsToChannel obj 0 vertColors
						
						--add uv2 if exist
						if nodesInfo[8].count>0 then(
							meshop.setNumMaps obj 3 keep:true
							BF2AddVertsToChannel obj 2 nodesInfo[8]
						)
						
						-- detach faces to different nodes
						convertToMesh obj
						local nodes = #()
						local blendFaces = #()
						--init nodes
						for k=geom[i].lod[j].nodenum to 1 by -1 do(
							nodes[k] = #()
						)
						
						--blendFaces
						for k=1 to nodesInfo[5].count do(
							if nodesInfo[5][k]>0 then append nodes[nodesInfo[5][k]] k
							else append blendFaces nodesInfo[2][k]
						)
						
						
						if blendFaces.count > 0 then(
							--format "Creating % blend faces...\n" blendFaces.count
							--detach blendFaces
							/*local detached = meshop.detachFaces obj blendFaces delete:false asMesh:true
							local blendMesh = Editable_mesh()
							blendMesh.mesh = detached
							update blendMesh
							blendMesh.name = "blendMesh"
							append lodDummy.children blendMesh
							blendMesh.material = multimat*/
							/*
							--get nodes for blendFaces, add bone and skin(skin is added on the original obj for convenience)
							local nodesForBlend = #()
							local vertsForBlend = #()
							local vertsNotForBlend = #()
							for k=1 to blendFaces.count do(
								for l=1 to 3 do(
									local vertIndex = blendFaces[k][l]
									--format "vertIndex: %\n" vertIndex
									local nodeIndex = nodesInfo[7][vertIndex]
									if (findItem nodesForBlend nodeIndex) == 0 then append nodesForBlend nodeIndex
									if (findItem vertsForBlend vertIndex) == 0 then append vertsForBlend vertIndex
									else append vertsNotForBlend vertIndex
								)	
							)
							format "nodes for blend faces: %\n" nodesForBlend
							--add bone
							local boneObjs = #()
							for k=1 to nodesForBlend.count do(
								local bonename = "geom" + ((i-1) as string) + "lod" + ((j-1) as string) + "node" + ((nodesForBlend[k]-1) as string)
								local boneObj = Pyramid name:bonename width:(0.03*s) depth:(0.03*s) height:(0.1*s)
								append lodDummy.children[nodesForBlend[k]].children boneObj
								--boneObjs[nodesForBlend[k]] boneObj
								append boneObjs boneObj
							)
							--add skin modifier to blendMesh
							addModifier obj (Skin())
							local skinMod = obj.modifiers[#skin]
							-- necessary for skinOps to work
							max modify mode
							select obj
							skinMod.bone_Limit = 1 -- only 1 bone affect one vert
							skinMod.weightAllVertices = off
							--add bones
							for l=1 to boneObjs.count do(
								if boneObjs[l]==undefined then continue
								local updateInt = 0
								if l==boneObjs.count then updateInt=-1 --update modifier at the last add
								skinOps.addbone skinMod boneObjs[l] updateInt
							)
							--add weight
							for l=1 to obj.numverts do(
								if (findItem vertsForBlend l) == 0 then continue
								local nodeIndex = nodesInfo[7][l] --blendindices[1]
								local boneIndex = findItem nodesForBlend nodeIndex 
								if boneIndex==0 then format "Error! index % not found in nodesForBlend!" boneInfo
								skinOps.SetVertexWeights skinMod l boneIndex 1.0
							)
							
							--delete unblended vertices(not sure if it works)
							--modPanel.setCurrentObject obj.baseObject
							--max create mode
							format "vertsNotForBlend: %\n" vertsNotForBlend
							meshop.deleteVerts $.baseObject vertsNotForBlend
							--deleteVert obj vertsNotForBlend
							update obj
							
							--material
							obj.material = multimat*/
							
							--a basic implementation -- import the whole object and skin it to nodes...(wonder if it works when export)
							
							--create nodes dummys and attach a bone to each
							local boneObjs = #()
							for k=2 to geom[i].lod[j].nodenum do(
								local dummyMesh = mesh vertices:#([0.0,0.0,0.0],[0.0,0.0,0.0],[0.0,0.0,0.0]) faces:#([1,2,3]) --add dummy mesh
								dummyMesh.name = (k-1) as string
								if dummyMesh.name.count<2 then dummyMesh.name = "0"+dummyMesh.name
								append lodDummy.children dummyMesh
								dummyMesh.material = multimat
								--bone
								local bonename = "geom" + ((i-1) as string) + "lod" + ((j-1) as string) + "node" + ((k-1) as string)
								local boneObj = Pyramid name:bonename width:(0.03*s) depth:(0.03*s) height:(0.1*s)
								append boneObjs boneObj
								append dummyMesh.children boneObj								
							)
							
							--do not skin on obj directly to avoid loop independence error
							--clone obj to store skin data
							local skinobj = Editable_mesh()
							skinobj.mesh = obj.mesh
							skinobj.name = "skindata_" + "geom" + ((i-1) as string) + "lod" + ((j-1) as string)	
							append lodDummy.children skinobj
							
							--use obj itself as the base node(0)
							obj.name = "00"
							obj.wirecolor = (random [0,0,0] [1,1,1]) as color
							obj.material = multimat
							append lodDummy.children obj
							
							--add skin modifier to skinobj
							addModifier skinobj (Skin())
							local skinMod = skinobj.modifiers[#skin]
							-- necessary for skinOps to work
							max modify mode
							select skinobj
							skinMod.bone_Limit = 1 -- only 1 bone affect one vert
							skinMod.weightAllVertices = off
							
							--add bones
							for k=1 to boneObjs.count do(
								if boneObjs[k]==undefined then continue
								local updateInt = 0
								if k==boneObjs.count then updateInt=-1 --update modifier at the last add
								skinOps.addbone skinMod boneObjs[k] updateInt
							)
							
							--add weight
							for k=1 to skinobj.numverts do(
								local nodeIndex = nodesInfo[7][k] --blendindices[1]+1
								--local boneIndex = findItem nodesForBlend nodeIndex 
								--if boneIndex==0 then format "Error! index % not found in nodesForBlend!" boneInfo
								if nodeIndex>1 then skinOps.SetVertexWeights skinMod k (nodeIndex-1) 1.0
							)
							
							max create mode
							
						)
						--format "	obj vertices count: %\n	obj faces count: %\n	obj tverts count: %\n" obj.numverts obj.numfaces obj.numtverts
						else (
							--detach nodes
							for k=1 to geom[i].lod[j].nodenum do(
								--format "nodes[%].count= %\n" k nodes[k].count
								local detached = meshop.detachFaces obj nodes[k] delete:false asMesh:true
								local newMesh = Editable_mesh()
								newMesh.mesh = detached
								newMesh.wirecolor = (random [0,0,0] [1,1,1]) as color
								update newMesh
								newMesh.name = (k-1) as string
								if newMesh.name.count<2 then newMesh.name = "0"+newMesh.name
								append lodDummy.children newMesh
								--apply material
								newMesh.material = multimat
								--format "	newMesh vertices count: %\n	newMesh faces count: %\n	newMesh tverts count: %\n" newMesh.numverts newMesh.numfaces newMesh.numtverts
							)
							delete obj
						)
							
					)
					else(
						--use transformed node
						messagebox "Staticmeshs not supported yet!"
					)
				)
				format "  lod % end\n" (j-1)
			)
			format " geom % end\n" (i-1)
		)	
		format "End importing..root:%\n" rootDummy
		return rootDummy
	)
)

fn bf2ImportMeshFile_new filename forLightmap s=
(
	if filename == undefined then filename = getOpenFileName()
	if filename == undefined then return undefined
	local filetype = lowercase (getFilenameType filename)
	local meshinstance = bf2mesh()
	format "filetype: %\n" filetype
	case filetype of(
		".bundledmesh": meshinstance.isBundledMesh = true
		".skinnedmesh": meshinstance.isSkinnedMesh = true
		".staticmesh": format "staticmesh\n"
		default: (
			messagebox "Not a bf2 mesh file!"
			return undefined
		)
	)
	rollout rolWorkingStatus "Importing...." width:224 height:80
	(
		button btn1 "Please Wait ..." pos:[32,24] width:152 height:40 enabled:false
		
	)
	--bf2mdtOps.importMeshFile filename forLightmap s
	--bf2file = fopen filename "rb"
	createDialog rolWorkingStatus style:#(#style_sysmenu, #style_titlebar)
	meshinstance.Load filename
	local rootNode = (meshinstance.createInstance forLightmap s)
	destroyDialog rolWorkingStatus
	return rootNode
)

--test
--bf2ImportMeshFile_new undefined 0 10
worldlife is offline Reply With Quote
Old 2017-08-12, 07:36   #37
[R-CON]​Harmonikater
PR:BF2 Contributor
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

Hm, regarding the BundledMesh Skin import, I'm actually not sure anymore it's possible to always get it correctly, without having everything remain one single mesh (as you're doing right now). Since really all the skin mod does on export is change which node the vertex belongs to, leaving no information which it belonged to originally in the scene.
I guess maybe if we do weld identical vertices and then afterwards split off only those vertices belonging to a node other then the main node that represent an independent element. Assuming of course that skin is only ever applied to nodes that are connected to the main mesh, which should mostly be the case for tank threads.
However mostly not the case for dampers on retractable landing gears on aircraft, I don't think we can get those correctly at all...

Also, skinnedmesh import seems to work great! I'll try to integrate that somehow, since I think it will be useful for 3P animating.
Harmonikater is offline Reply With Quote
Old 2017-08-12, 08:37   #38
Outlawz7
Retired PR Developer

Outlawz7's Avatar
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

I thought of a small feature request. In Rhino's LM tutorial there's a section where staticobjects.con is imported but you have to move the folders out so 3ds Max doesn't import actual meshes, just positions as helpers. Could you add that as a script, importing positions only from staticobjects.con? I know you improved importing OG for LMing here, but I found other uses for importing positions.

Outlawz7 is offline Reply With Quote
Old 2017-08-12, 15:16   #39
FalkeS
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

This is great!
FalkeS is offline Reply With Quote
Old 2017-08-14, 10:34   #40
[R-DEV]Mats391
PR:BF2 Lead Developer
Supporting Member
PR Server License Administrator

Mats391's Avatar
Default Re: Experimental updated 3dsMax Tools. Newer Max versions support and more.

The new animation tools are really nice. I am no animator myself, but since we currently have none I often have to do minor adjustments and fixes for new weapons and such. The new tools will help a lot with that especially having the soldier mesh in 3p, something our past animators never bothered to add to their scenes
I have one issue tho, I cannot create templates.

How do I create the first template?

Mineral: TIL that Wire-guided missiles actually use wire
Mats391 is offline Reply With Quote
Reply


Tags
3dsmax, experimental, max, newer, support, tools, updated, vbf2 or pr, versions

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off



All times are GMT. The time now is 09:24.