Home Technical Talk

3ds max automatic backface deletion?

I was wondering if there was a script to automatically (and reliably!) delete backfaces based on a moving camera ( or cameras)? It seems like a great task for a script or a robot, but here we are doing it by hand...
It makes no sense.

Anyone?

Replies

  • renderhjs
    Options
    Offline / Send Message
    renderhjs sublime tool
    polyboost or max2010 lets you select faces that are facing to the perspective, with ctrl + i you can select the inverse.
    It ccould be though that you need to do that multiple times with a moving camera and perhaps write some script that merges the inverted selections to a whole.
  • SHEPEIRO
    Options
    Offline / Send Message
    SHEPEIRO polycounter lvl 17
    why not addative select the polys at multiple points along the cameras path, then invert to get the ones not needed, did this befor and it seamed to work quite well, but it depends on how complex the scene is and how many camera veiws you have
  • SyncViewS
    Options
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi MightyPea,
    here is a function to do the job: feed it with an Editable Poly/Mesh, a camera and a frame range, and it will select all faces facing backward relative to camera in every frame of specified range. Let me know if you need a Floater with some interface.
    function getBackFacesInRange obj camScan iFrameA iFrameZ =
    (
        local p3ViewDir = [0,0,0]
        local p3ViewPos = [0,0,0] 
    
        local p3FaceCenter = [0,0,0]
        local p3ScanDir = [0,0,0]
    
        if (classOf obj == Editable_Poly) then
        (
            local baFaces = #{1..(polyOp.getNumFaces obj)}
            local baBackFaces = baFaces
            local baFrameBackFaces = #{}
            
            for iFrame = iFrameA to iFrameZ by 1 do
            (
                at time iFrame
                (
                    p3ViewDir = -camScan.transform.row3
                    p3ViewPos = camScan.position
        
                    baFrameBackFaces = #{}
            
                    for iFace in baFaces do
                    (
                        p3FaceCenter = polyOp.getSafeFaceCenter obj iFace
                        if (p3FaceCenter == undefined) then
                            p3FaceCenter = polyOp.getFaceCenter obj iFace
                        
                        if (camScan.orthoProjection) then
                            p3ScanDir = p3ViewDir
                        else
                            p3ScanDir = normalize (p3FaceCenter - p3ViewPos)
                            
                        p3FaceNormal = polyOp.getFaceNormal obj iFace
                        
                        if ((dot p3FaceNormal p3ScanDir) > 0) then
                            baFrameBackFaces[iFace] = true
                    )
                    baBackFaces *= baFrameBackFaces
                )
            )
            -- for visual feedback only
            polyOp.setFaceSelection obj baBackFaces
        )
        else if (classOf obj == Editable_Mesh) then
        (
            local baFaces = #{1..(meshOp.getNumFaces obj)}
            local baBackFaces = baFaces
            local baFrameBackFaces = #{}
            
            for iFrame = iFrameA to iFrameZ by 1 do
            (
                at time iFrame
                (
                    p3ViewDir = -camScan.transform.row3
                    p3ViewPos = camScan.position
        
                    baFrameBackFaces = #{}
            
                    for iFace in baFaces do
                    (
                        p3FaceCenter = meshOp.getFaceCenter obj iFace
                        
                        if (camScan.orthoProjection) then
                            p3ScanDir = p3ViewDir
                        else
                            p3ScanDir = normalize (p3FaceCenter - p3ViewPos)
                            
                        p3FaceNormal = getFaceNormal obj iFace
                        
                        if ((dot p3FaceNormal p3ScanDir) > 0) then
                            baFrameBackFaces[iFace] = true
                    )
                    baBackFaces *= baFrameBackFaces
                )
            )
            -- for visual feedback only
            setFaceSelection obj baBackFaces
        )
        else
        (
            throw "Wrong input in function getBackFacesInRange()"
        )
    
        return baBackFaces
    )
    
  • SyncViewS
    Options
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi again,
    I had some free time, so here is the script with a simple interface.
    rollout rolTest "Sel Back Faces"
    (
    
    -- Filter Functions ------------------------------------------------------------
    
        function filterObj item = ( ((classOf item) == Editable_Poly) or ((classOf item) == Editable_Mesh) )
    
        function filterCamera item = ((superClassOf item) == Camera)
    
    -- Interface -------------------------------------------------------------------
    
        pickButton pbtObj "Object" message:"Pick an Editable Poly or Editable Mesh" filter:filterObj autoDisplay:true width:90 height:16 align:#left offset:[-6, 14] toolTip:"Editable Poly / Mesh"
        pickButton pbtCamera "Camera" message:"Pick a Camera" filter:filterCamera autoDisplay:true width:90 height:16 align:#left offset:[-6, -2] toolTip:"Camera"
        groupBox gbxNodes " Nodes " width:98 height:56 align:#left offset:[-10, -56]
    
        label lbFrameA "From" align:#left offset:[-4, 15] across:2
        spinner spFrameA range:[-99999, 99999, 0] type:#integer fieldWidth:45 align:#left offset:[-11, 14]
        label lbFrameZ "To" align:#left offset:[-4, -1] across:2
        spinner spFrameZ range:[-99999, 99999, 100] type:#integer fieldWidth:45 align:#left offset:[-11, -2]
        groupBox gbxFrames " Frame Span " width:98 height:56 align:#left offset:[-10, -56]
    
        progressBar pbPercent width:98 height:7 value:0 color:(color 0 197 0) align:#center offset:[0, -2]
        button btSelect "Select Faces" width:98 height:16 align:#center offset:[0, -5]
    
    -- Functions -------------------------------------------------------------------
    
        function getBackFacesInRange obj camScan iFrameA iFrameZ &progress: =
        (
            local p3ViewDir = [0,0,0]
            local p3ViewPos = [0,0,0]
    
            local p3FaceCenter = [0,0,0]
            local p3ScanDir = [0,0,0]
    
            if (classOf obj == Editable_Poly) then
            (
                local baFaces = #{1..(polyOp.getNumFaces obj)}
                local baBackFaces = baFaces
                local baFrameBackFaces = #{}
    
                for iFrame = iFrameA to iFrameZ by 1 do
                (
                    at time iFrame
                    (
                        p3ViewDir = -camScan.transform.row3
                        p3ViewPos = camScan.position
    
                        baFrameBackFaces = #{}
    
                        for iFace in baFaces do
                        (
                            p3FaceCenter = polyOp.getSafeFaceCenter obj iFace
                            if (p3FaceCenter == undefined) then
                                p3FaceCenter = polyOp.getFaceCenter obj iFace
    
                            if (camScan.orthoProjection) then
                                p3ScanDir = p3ViewDir
                            else
                                p3ScanDir = normalize (p3FaceCenter - p3ViewPos)
    
                            p3FaceNormal = polyOp.getFaceNormal obj iFace
    
                            if ((dot p3FaceNormal p3ScanDir) > 0) then
                                baFrameBackFaces[iFace] = true
                        )
                        baBackFaces *= baFrameBackFaces
                    )
                    if (progress != unsupplied) then
                        progress = ((iFrame - iFrameA) as Float) / (iFrameZ - iFrameA) * 100
                )
                polyOp.setFaceSelection obj baBackFaces
                forceCompleteRedraw()
            )
            else if (classOf obj == Editable_Mesh) then
            (
                local baFaces = #{1..(meshOp.getNumFaces obj)}
                local baBackFaces = baFaces
                local baFrameBackFaces = #{}
    
                for iFrame = iFrameA to iFrameZ by 1 do
                (
                    at time iFrame
                    (
                        p3ViewDir = -camScan.transform.row3
                        p3ViewPos = camScan.position
    
                        baFrameBackFaces = #{}
    
                        for iFace in baFaces do
                        (
                            p3FaceCenter = meshOp.getFaceCenter obj iFace
    
                            if (camScan.orthoProjection) then
                                p3ScanDir = p3ViewDir
                            else
                                p3ScanDir = normalize (p3FaceCenter - p3ViewPos)
    
                            p3FaceNormal = getFaceNormal obj iFace
    
                            if ((dot p3FaceNormal p3ScanDir) > 0) then
                                baFrameBackFaces[iFace] = true
                        )
                        baBackFaces *= baFrameBackFaces
                    )
                    if (progress != unsupplied) then
                        progress = ((iFrame - iFrameA) as Float) / (iFrameZ - iFrameA) * 100
                )
                setFaceSelection obj baBackFaces
                forceCompleteRedraw()
            )
            else
            (
                throw "Wrong input in function getBackFacesInRange()"
            )
        )
    
    -- Events ----------------------------------------------------------------------
    
        on pbtObj rightClick do
            pbtObj.object = undefined
    
        on pbtCamera rightClick do
            pbtCamera.object = undefined
    
        on btSelect pressed do
            if ( (pbtObj.object != undefined) and (pbtCamera.object != undefined) ) then
            (
                getBackFacesInRange pbtObj.object pbtCamera.object spFrameA.value spFrameZ.value progress:&pbPercent.value
                pbPercent.value = 0
            )
    
    ) -- End Rollout
    
    createDialog rolTest 104 147 style:#(#style_toolwindow, #style_border, #style_sysmenu)
    
  • KikiAlex
    Options
    Offline / Send Message
    Here is my version of this thing ,it works on Editable Poly only with no instances, it evaluates all the geometry in the scene
    /*****************************************************************************************************************
    Made by: Popescu Alexandru Cristian (KIKI) kiki_karon@yahoo.com
    Script: Camera_Culling v10.
    Description : It tryes to delete the sceen tris that are not visible for the selected camera.
    ******************************************************************************************************************/
    
    struct	TKey
    (
    	Number,
    	objectList
    )
    
    struct 	TObject
    (
    	objectName,
    	visFaces
    )
    
    --make all names in scene unique
    function polyop_UniqueName=
    (
    	for o in objects do
    	(
    		if ((classof o) == Editable_Poly) do
    		(
    			o.name = (uniquename o.name);
    		)
    	)
    )
    
    --break grup
    function break_Grup =
    (
    	for o in objects do
    	(
    		if (isGroupHead o) do
    		(
    			ungroup o
    		)
    	)
    )
    
    function object_Delete_Vertex0_objects object_list =
    (
    	for o in objects do
    	(
    		if ( (classof o) == Editable_Poly)	do
    		( 
    			if (o.Verts.Count < 0) do
    			(
    				delete o
    			)
    		)
    	)
    ) 
    
    --test if the object bounding sphere is in camera bounding sphere
    function object_in_cameraspace camera obj =
    (
    	tanC = tan (camera.FOV /2);
    	obj_center = obj.center * (inverse camera.transform);
    	obj_centerZ = -obj_center.z;
    	obj_center = ([1,1,1] + obj_center) / obj_centerZ/ tanC;
    	obj_center.y = obj_center.y*(getRendImageAspect());
    	obj_max = obj.max * (inverse camera.transform);
    	obj_maxZ = -obj_max.z;
    	obj_max = ([1,1,1] + obj_max ) / obj_maxZ/ tanC;
    	obj_max.y = obj_max.y*(getRendImageAspect());
    	obj_distance = (distance obj_center obj_max);
    	if (distance [obj_center.x,obj_center.y] [0,0]) < (obj_distance + 1.5) do
    	(
    		return true;
    	)
    	return false;
    )
    
    
    function Select_Visible_Poly camera obj BFC_factor = 
    (
    	vis_poly = #()
    	the_tan = (tan (camera.FOV/2));
    	for p in obj.Faces do
    	(
    		--BFC
    		normal = polyop.GetFaceNormal obj p.index
    		lookAtCamera = dot camera.dir normal
    		if (lookAtCamera > BFC_factor) do
    		(
    			verts = (polyop.getVertsUsingFace obj p)
    			vis_Verts = false;
    			ScreenSpace_Verts = #()
    			----
    			iterate_in_verts = true;
    			for v in verts do
    			(
    				if iterate_in_verts do
    				(
    				--world pos
    				pos_W_v = polyop.getVert obj v;
    				--camera pos
    				pos_C_v = (pos_W_v * (inverse camera.transform));
    				--screen space
    				if (pos_C_v.z < 0) do
    					(
    					the_z = -pos_C_v.z 
    					pos_S_v =  ([1,1,1] + pos_C_v)/ the_z / the_tan;
    					pos_S_v.y = pos_S_v.y*(getRendImageAspect());
    					--for the polys with verts outside of the frustum test
    					append ScreenSpace_Verts pos_S_v;
    					--Frustum
    					testA = ((pos_S_v.x < 1) and (pos_S_v.x > -1) and (pos_S_v.y < 1) and (pos_S_v.y > -1)) 
    					if testA do
    						(
    						--append vis_Verts v;
    						vis_Verts = true;
    						--the calculations become irelevant after one relevant vert
    						iterate_in_verts = false;
    						--print v
    						)
    					)
    				)
    			)
    			--test for polys with verts outside of the frustum
    			if (iterate_in_verts == true) do
    				(
    					Verts_Type = #()
    					for ssp in ScreenSpace_Verts do
    					(
    						--XP = 	
    						if (ssp.x > 1) and (ssp.y < 1)  and (ssp.y > -1) do
    						(
    							append Verts_Type 1
    						)
    						--XN =	
    						if (ssp.x < -1) and (ssp.y < 1)  and (ssp.y > -1)  do
    						(
    							append Verts_Type 2
    						)
    						--YP =
    						if (ssp.y > 1) and (ssp.x < 1)  and (ssp.x > -1) do
    						(
    							append Verts_Type 3
    						)
    						--YN = 	
    						if (ssp.y < -1) and (ssp.x < 1)  and (ssp.x > -1) do
    						(
    							append Verts_Type 4
    						)
    						--CXP1
    						if (ssp.x > 1) and (ssp.y > 1)  and (ssp.y > -1) do
    						(
    							append Verts_Type 5
    						)
    						--CXP2
    						if (ssp.x > 1) and (ssp.y < 1)  and (ssp.y < -1) do
    						(
    							append Verts_Type 6
    						)
    						--CXNP1
    						if (ssp.x < -1) and (ssp.y > 1)  and (ssp.y > -1) do
    						(
    							append Verts_Type 7
    						)
    						--CXNP2
    						if (ssp.x < -1) and (ssp.y < 1)  and (ssp.y < -1) do
    						(
    							append Verts_Type 8
    						)
    					)
    					
    					for ver_tC = 1 to Verts_Type.Count do
    					(
    						ver_t = Verts_Type[ver_tC];
    						for ver_t2C = 1 to Verts_Type.Count do
    						(
    							if (ver_tC != ver_t2C) do
    							(
    								ver_t2 = Verts_Type[ver_t2C];
    								if (ver_t == 1) and ((ver_t2 == 2) or (ver_t2 == 8) or (ver_t2 == 7) or (ver_t2 == 3) or (ver_t2 == 4)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 2) and ((ver_t2 == 1) or (ver_t2 == 3) or (ver_t2 == 4) or (ver_t2 == 5) or (ver_t2 == 6)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 3) and ((ver_t2 == 1) or (ver_t2 == 2) or (ver_t2 == 5) or (ver_t2 == 4) or (ver_t2 == 7)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 4) and ((ver_t2 == 1) or (ver_t2 == 6) or (ver_t2 == 3) or (ver_t2 == 8) or (ver_t2 == 2)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 5) and ((ver_t2 == 3) or (ver_t2 == 8) or (ver_t2 == 2)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 6) and ((ver_t2 == 4) or (ver_t2 == 7) or (ver_t2 == 2)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 7) and ((ver_t2 == 1) or (ver_t2 == 6) or (ver_t2 == 3)) do
    								(
    									vis_Verts = true;
    								)
    								if (ver_t == 8) and ((ver_t2 == 1) or (ver_t2 == 5) or (ver_t2 == 4)) do
    								(
    									vis_Verts = true;
    								)
    							)
    						)
    					)
    				Verts_Type = undefined;
    				)
    		if (vis_Verts == true) do
    				(
    					append vis_poly p.index;
    				)
    		ScreenSpace_Verts = undefined;
    		)
    		
    	)
    	--polyop.SetFaceSelection obj vis_poly
    	--cleen used memory
    	GC();
    	return vis_poly;
    )
    
    
    function polyop_SetTKey frameNum CameraI Threshold =
    (
    	sliderTime = (frameNum as time);
    	curent_key = TKey();
    	curent_key.Number = frameNum;
    	curent_key.objectList = #();
    	for o in objects do
    	(
    		if (((classof o) == Editable_Poly) and (object_in_cameraspace CameraI o)) do
    		(
    		curent_obj = TObject();
    		curent_obj.objectName = o.name;
    		curent_obj.visFaces = (Select_Visible_Poly CameraI o Threshold)
    		append 	curent_key.objectList curent_obj;
    		)
    	)
    	return curent_key 
    )
    
    function polyop_Generate_TKeyArray Camera  BFC_Threshold  fstar fend =
    (
    	if (fstar == fend) then
    	(
    		animationRange = interval fstar (fend+1);
    	)
    	else
    	(
    		animationRange = interval fstar fend;
    	)
    	--we are forring through this values so the fstar should allways be smaller then fend
    	if (fstar > fend) do
    	(
    		temp = fstar;
    		fstar = fend;
    		fend = temp;
    	)
    	total_range =  (fend - fstar);
    	TKeyArray = #()
    	progressStart "Culling";
    	progressUpdate 1;
    	for t = (fstar) to (fend) do
    	(
    	range = (t - fstar)
    	ratio = 1;
    	if (range > 0) do
    		(
    			ratio = (100*range)/total_range;
    		)
    	progres = (ratio);
    	cont = progressUpdate (progres);
    	if (not(cont)) do
    		(
    		progressEnd();
    		return TKeyArray;
    		)
    	Tkey_t  = polyop_SetTKey t Camera BFC_Threshold ;
    	append TKeyArray Tkey_t;
    	)
    	progressEnd();
    	return TKeyArray 
    )
    
    function polyop_Generate_VisibleFaceSelection  Camera  BFC_Threshold  fstar fend =
    (
    	lTKeyArray = polyop_Generate_TKeyArray Camera  BFC_Threshold fstar fend ;
    	for o in objects do
    	(
    		if ((classof o) == Editable_Poly) do
    		(
    			--
    			finalSelection = #()
    			--
    			for lTkey in lTKeyArray do
    			(
    			for lTObject in lTkey.objectList do
    				(
    					if (lTObject.objectName == o.name) do
    					(
    					for sf in lTObject.visFaces do
    						(
    							if ((findItem finalSelection sf) == 0) do
    							(
    							append finalSelection sf
    							)
    						)
    					)
    				)
    			)
    		polyop.SetFaceSelection o finalSelection; 
    		)
    	)
    )
    
    function polyop_Delete_non_Selected_Faces object_list =
    (
    	for o in object_list do
    		(
    		if ((classof o) == Editable_Poly) do
    		(
    				deleted_faces = #();
    				Selected_Faces2 = ((polyop.GetFaceSelection o) as array); 
    				for F  in o.Faces do
    				(
    					if ((findItem Selected_Faces2 F.index) == 0) do
    					(
    					append  deleted_faces F.index;
    					)
    				)
    				polyOP.DeleteFaces o deleted_faces delIsoVerts:true;
    			)
    		)
    )
    
    function polyop_Delete_non_Visible_Polys Camera  BFC_Threshold  fstar fend =
    (
    	break_Grup();
    	polyop_UniqueName();
    	polyop_Generate_VisibleFaceSelection Camera  BFC_Threshold  fstar fend ;
    	--sometimes max misses a few things, call the cleen functions more then oance...
    	polyop_Delete_non_Selected_Faces objects;
    	polyop_Delete_non_Selected_Faces objects;
    	object_Delete_Vertex0_objects objects;
    	object_Delete_Vertex0_objects objects;
    	object_Delete_Vertex0_objects objects;
    )
    
    --Select_Visible_Poly    -0.2
    
    --interface
    rollout cullingRollout "Camera Culling rev 10.0" width:240 height:230
    (
    	button btn1 "Delete invisible polys for the selected camera" pos:[8,114] width:225 height:36
    	pickbutton select_cameraB "Select the camera" pos:[8,74] width:225 height:36
    	spinner bfc_t "BFC threshold" pos:[48,10] width:90 height:16 enabled:true range:[-1,1,-0.2] type:#float scale:0.01
    	spinner frame_start "Start Frame    " pos:[48,32] width:90 height:16 enabled:true range:[-100000,100000,0] type:#integer scale:1
    	spinner frame_end "End Frame     " pos:[48,54] width:90 height:16 enabled:true range:[-100000,100000,100] type:#integer scale:1
    	label lbl1 " Go to: n Edit->Fetch for undon --------------------------------n Popescu Alexandru Cristian n kiki_karon@yahoo.com" pos:[5,154] width:311 height:80
    
    	on cullingRollout close do
    	(
    	global cullingRollout_available_hash123rttyh2 = false;
    	)
    	
    	on select_cameraB picked obj do
    	(
    	if (((classof obj) == Freecamera ) or ((classof obj) == Targetcamera)) do
    		(
    		select_cameraB.text = obj.name
    		)
    	)
    	
    	on btn1 pressed do
    	(
    		if (select_cameraB.text != "Select the camera") do
    		(
    			if (isDeleted select_cameraB.object) do
    			(
    				select_cameraB.text = "Select the camera";
    			)
    			if ((not(isDeleted select_cameraB.object)) and (select_cameraB.object != undefined) and (select_cameraB.text != "Select the camera")) do
    			( 
    				holdMaxFile();
    				init_num_poly = 0;
    				for o in objects do
    				(
    				init_num_poly += (GetTriMeshFaceCount o)[1];
    				)
    				if (heapSize < 32000000) do
    					(
    						--set the max memory the script can use to 32MB;
    						heapSize = 32000000; 
    					)
    				--this is a max-shit safe frame problem haxor fix
    				old_fov = select_cameraB.object.FOV ;
    				select_cameraB.object.FOV = select_cameraB.object.FOV + (select_cameraB.object.FOV * 0.1);
    				--
    				polyop_Delete_non_Visible_Polys select_cameraB.object bfc_t.Value frame_start.Value frame_end.Value;
    				select_cameraB.object.FOV = old_fov;
    				final_num_poly = 0;
    				for o in objects do
    				(
    				final_num_poly += (GetTriMeshFaceCount o)[1];
    				)
    				MessageBox ("Number of triangles deleted :" + ((init_num_poly - final_num_poly) as string)) title:"Culling" beep:false ;
    				object_Delete_Vertex0_objects objects;
    			)
    		)
    	)
    )
    
    
    --only one instance of this dialog available
    if (cullingRollout_available_hash123rttyh2 != true) do
    (
    createdialog cullingRollout;
    global cullingRollout_available_hash123rttyh2 = true;
    )
    --If you see any problems with this script, and you have the time, write me an email (kiki_karon@yahoo.com)
    /*
    -This is versio 9 of this script,the first official version
     -We are now at version 10 of this script,function object_in_cameraspace was added, and used in polyop_SetTKey,it is a simple bounding sphere test,object versus camera space
    */
    --If you see any problems with this script, and you have the time, write me an email (kiki_karon@yahoo.com)
    
  • monster
    Options
    Offline / Send Message
    monster polycounter
    http://www.scriptspot.com/3ds-max/polyvis

    When I worked on Elite Force 2 (first person shooter), I used this script to delete polygons that never faced the camera during animation.

    The script may need to be updated a little, since it's a little old....
  • RazorBladder
    Options
    Offline / Send Message
    RazorBladder polycounter lvl 18
    Real nice script Sync, I'm wondering if it's at all possible to write a script that can take multiple camera paths into account?
  • SyncViewS
    Options
    Offline / Send Message
    SyncViewS polycounter lvl 13
    Hi Razor, this is quite an ancient thread. I almost forgot about this script. I'm quite sure it is possible to take into account multiple cameras.

    You must be aware that this script takes into account only camera view direction, and not camera frustum. It is something I'd like to code and include in the new set of instrument I'm currently developing. I've just taken note about your request and will do my best to add it to upcoming version. Thank you.

    Anyway if you need it promptly, I can update this one to multiple cameras. Please let me know.
Sign In or Register to comment.