| 
									
										
										
										
											2009-11-01 15:21:20 +00:00
										 |  |  | # ##### BEGIN GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | #  modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | #  as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | #  of the License, or (at your option) any later version. | 
					
						
							|  |  |  | #  | 
					
						
							|  |  |  | #  This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | #  GNU General Public License for more details. | 
					
						
							|  |  |  | #  | 
					
						
							|  |  |  | #  You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | #  along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  | #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # ##### END GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | import bpy | 
					
						
							| 
									
										
										
										
											2009-09-23 01:59:57 +00:00
										 |  |  | import sys, os, re | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | import http, http.client, http.server, urllib | 
					
						
							|  |  |  | import subprocess, shutil, time, hashlib | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-01 18:57:22 +00:00
										 |  |  | import netrender.model | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | import netrender.slave as slave | 
					
						
							|  |  |  | import netrender.master as master | 
					
						
							|  |  |  | from netrender.utils import * | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-09 01:52:57 +00:00
										 |  |  | def addFluidFiles(job, path): | 
					
						
							|  |  |  | 	if os.path.exists(path): | 
					
						
							|  |  |  | 		pattern = re.compile("fluidsurface_(final|preview)_([0-9]+)\.(bobj|bvel)\.gz") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for fluid_file in sorted(os.listdir(path)): | 
					
						
							|  |  |  | 			match = pattern.match(fluid_file) | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			if match: | 
					
						
							|  |  |  | 				current_frame = int(match.groups()[1]) | 
					
						
							|  |  |  | 				job.addFile(path + fluid_file, current_frame, current_frame) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def addPointCache(job, ob, point_cache, default_path): | 
					
						
							|  |  |  | 	if not point_cache.disk_cache: | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	name = point_cache.name | 
					
						
							|  |  |  | 	if name == "": | 
					
						
							|  |  |  | 		name = "".join(["%02X" % ord(c) for c in ob.name]) | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2009-11-03 18:08:25 +00:00
										 |  |  | 	cache_path = bpy.utils.expandpath(point_cache.filepath) if point_cache.external else default_path | 
					
						
							| 
									
										
										
										
											2009-10-09 01:52:57 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	index = "%02i" % point_cache.index | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if os.path.exists(cache_path): | 
					
						
							|  |  |  | 		pattern = re.compile(name + "_([0-9]+)_" + index + "\.bphys") | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		cache_files = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for cache_file in sorted(os.listdir(cache_path)): | 
					
						
							|  |  |  | 			match = pattern.match(cache_file) | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			if match: | 
					
						
							|  |  |  | 				cache_frame = int(match.groups()[0]) | 
					
						
							|  |  |  | 				cache_files.append((cache_frame, cache_file)) | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 		cache_files.sort() | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		if len(cache_files) == 1: | 
					
						
							|  |  |  | 			cache_frame, cache_file = cache_files[0] | 
					
						
							|  |  |  | 			job.addFile(cache_path + cache_file, cache_frame, cache_frame) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			for i in range(len(cache_files)): | 
					
						
							|  |  |  | 				current_item = cache_files[i] | 
					
						
							|  |  |  | 				next_item = cache_files[i+1] if i + 1 < len(cache_files) else None | 
					
						
							|  |  |  | 				previous_item = cache_files[i - 1] if i > 0 else None | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				current_frame, current_file = current_item | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				if  not next_item and not previous_item: | 
					
						
							|  |  |  | 					job.addFile(cache_path + current_file, current_frame, current_frame) | 
					
						
							|  |  |  | 				elif next_item and not previous_item: | 
					
						
							|  |  |  | 					next_frame = next_item[0] | 
					
						
							|  |  |  | 					job.addFile(cache_path + current_file, current_frame, next_frame - 1) | 
					
						
							|  |  |  | 				elif not next_item and previous_item: | 
					
						
							|  |  |  | 					previous_frame = previous_item[0] | 
					
						
							|  |  |  | 					job.addFile(cache_path + current_file, previous_frame + 1, current_frame) | 
					
						
							|  |  |  | 				else: | 
					
						
							|  |  |  | 					next_frame = next_item[0] | 
					
						
							|  |  |  | 					previous_frame = previous_item[0] | 
					
						
							|  |  |  | 					job.addFile(cache_path + current_file, previous_frame + 1, next_frame - 1) | 
					
						
							|  |  |  | 						 | 
					
						
							| 
									
										
										
										
											2009-10-01 18:57:22 +00:00
										 |  |  | def clientSendJob(conn, scene, anim = False): | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 	netsettings = scene.network_render | 
					
						
							|  |  |  | 	job = netrender.model.RenderJob() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	if anim: | 
					
						
							|  |  |  | 		for f in range(scene.start_frame, scene.end_frame + 1): | 
					
						
							|  |  |  | 			job.addFrame(f) | 
					
						
							|  |  |  | 	else: | 
					
						
							|  |  |  | 		job.addFrame(scene.current_frame) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	filename = bpy.data.filename | 
					
						
							|  |  |  | 	job.addFile(filename) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	job_name = netsettings.job_name | 
					
						
							|  |  |  | 	path, name = os.path.split(filename) | 
					
						
							| 
									
										
										
										
											2009-10-09 01:52:57 +00:00
										 |  |  | 	path += os.sep | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 	if job_name == "[default]": | 
					
						
							|  |  |  | 		job_name = name | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2009-09-09 20:56:49 +00:00
										 |  |  | 	########################### | 
					
						
							|  |  |  | 	# LIBRARIES | 
					
						
							|  |  |  | 	########################### | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 	for lib in bpy.data.libraries: | 
					
						
							| 
									
										
										
										
											2009-11-03 18:08:25 +00:00
										 |  |  | 		job.addFile(bpy.utils.expandpath(lib_path)) | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2009-09-09 20:56:49 +00:00
										 |  |  | 	########################### | 
					
						
							|  |  |  | 	# IMAGES | 
					
						
							|  |  |  | 	########################### | 
					
						
							|  |  |  | 	for image in bpy.data.images: | 
					
						
							|  |  |  | 		if image.source == "FILE" and not image.packed_file: | 
					
						
							| 
									
										
										
										
											2009-11-03 18:08:25 +00:00
										 |  |  | 			job.addFile(bpy.utils.expandpath(image.filename)) | 
					
						
							| 
									
										
										
										
											2009-10-09 01:52:57 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	########################### | 
					
						
							|  |  |  | 	# FLUID + POINT CACHE | 
					
						
							|  |  |  | 	########################### | 
					
						
							|  |  |  | 	root, ext = os.path.splitext(name) | 
					
						
							|  |  |  | 	default_path = path + "blendcache_" + root + os.sep # need an API call for that | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for object in bpy.data.objects: | 
					
						
							|  |  |  | 		for modifier in object.modifiers: | 
					
						
							|  |  |  | 			if modifier.type == 'FLUID_SIMULATION' and modifier.settings.type == "DOMAIN": | 
					
						
							| 
									
										
										
										
											2009-11-03 18:08:25 +00:00
										 |  |  | 				addFluidFiles(job, bpy.utils.expandpath(modifier.settings.path)) | 
					
						
							| 
									
										
										
										
											2009-10-09 01:52:57 +00:00
										 |  |  | 			elif modifier.type == "CLOTH": | 
					
						
							|  |  |  | 				addPointCache(job, object, modifier.point_cache, default_path) | 
					
						
							|  |  |  | 			elif modifier.type == "SOFT_BODY": | 
					
						
							|  |  |  | 				addPointCache(job, object, modifier.point_cache, default_path) | 
					
						
							|  |  |  | 			elif modifier.type == "SMOKE" and modifier.smoke_type == "TYPE_DOMAIN": | 
					
						
							|  |  |  | 				addPointCache(job, object, modifier.domain_settings.point_cache_low, default_path) | 
					
						
							|  |  |  | 				if modifier.domain_settings.highres: | 
					
						
							|  |  |  | 					addPointCache(job, object, modifier.domain_settings.point_cache_high, default_path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		# particles modifier are stupid and don't contain data | 
					
						
							|  |  |  | 		# we have to go through the object property | 
					
						
							|  |  |  | 		for psys in object.particle_systems: | 
					
						
							|  |  |  | 			addPointCache(job, object, psys.point_cache, default_path) | 
					
						
							| 
									
										
										
										
											2009-09-09 20:56:49 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	# print(job.files) | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 	 | 
					
						
							|  |  |  | 	job.name = job_name | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	for slave in scene.network_render.slaves_blacklist: | 
					
						
							|  |  |  | 		job.blacklist.append(slave.id) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	job.chunks = netsettings.chunks | 
					
						
							|  |  |  | 	job.priority = netsettings.priority | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	# try to send path first | 
					
						
							| 
									
										
										
										
											2009-09-19 22:11:26 +00:00
										 |  |  | 	conn.request("POST", "/job", repr(job.serialize())) | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 	response = conn.getresponse() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	job_id = response.getheader("job-id") | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	# if not ACCEPTED (but not processed), send files | 
					
						
							|  |  |  | 	if response.status == http.client.ACCEPTED: | 
					
						
							| 
									
										
										
										
											2009-09-11 18:31:51 +00:00
										 |  |  | 		for filepath, start, end in job.files: | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 			f = open(filepath, "rb") | 
					
						
							| 
									
										
										
										
											2009-09-19 22:11:26 +00:00
										 |  |  | 			conn.request("PUT", "/file", f, headers={"job-id": job_id, "job-file": filepath}) | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 			f.close() | 
					
						
							|  |  |  | 			response = conn.getresponse() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	# server will reply with NOT_FOUD until all files are found | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	return job_id | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-10 20:41:18 +00:00
										 |  |  | def requestResult(conn, job_id, frame): | 
					
						
							| 
									
										
										
										
											2009-09-19 22:11:26 +00:00
										 |  |  | 	conn.request("GET", "/render", headers={"job-id": job_id, "job-frame":str(frame)}) | 
					
						
							| 
									
										
										
										
											2009-09-09 02:25:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-10 20:41:18 +00:00
										 |  |  | @rnaType | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | class NetworkRenderEngine(bpy.types.RenderEngine): | 
					
						
							| 
									
										
										
										
											2009-10-31 13:31:23 +00:00
										 |  |  | 	bl_idname = 'NET_RENDER' | 
					
						
							|  |  |  | 	bl_label = "Network Render" | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 	def render(self, scene): | 
					
						
							|  |  |  | 		if scene.network_render.mode == "RENDER_CLIENT": | 
					
						
							|  |  |  | 			self.render_client(scene) | 
					
						
							|  |  |  | 		elif scene.network_render.mode == "RENDER_SLAVE": | 
					
						
							|  |  |  | 			self.render_slave(scene) | 
					
						
							|  |  |  | 		elif scene.network_render.mode == "RENDER_MASTER": | 
					
						
							|  |  |  | 			self.render_master(scene) | 
					
						
							|  |  |  | 		else: | 
					
						
							|  |  |  | 			print("UNKNOWN OPERATION MODE") | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def render_master(self, scene): | 
					
						
							| 
									
										
										
										
											2009-09-15 19:53:18 +00:00
										 |  |  | 		netsettings = scene.network_render | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		address = "" if netsettings.server_address == "[default]" else netsettings.server_address | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		master.runMaster((address, netsettings.server_port), netsettings.server_broadcast, netsettings.path, self.update_stats, self.test_break) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	def render_slave(self, scene): | 
					
						
							|  |  |  | 		slave.render_slave(self, scene) | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	def render_client(self, scene): | 
					
						
							| 
									
										
										
										
											2009-09-01 01:09:05 +00:00
										 |  |  | 		netsettings = scene.network_render | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 		self.update_stats("", "Network render client initiation") | 
					
						
							|  |  |  | 		 | 
					
						
							| 
									
										
										
										
											2009-09-15 19:53:18 +00:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 		conn = clientConnection(scene) | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		if conn: | 
					
						
							|  |  |  | 			# Sending file | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			self.update_stats("", "Network render exporting") | 
					
						
							|  |  |  | 			 | 
					
						
							| 
									
										
										
										
											2009-09-01 01:09:05 +00:00
										 |  |  | 			job_id = netsettings.job_id | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 			 | 
					
						
							|  |  |  | 			# reading back result | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			self.update_stats("", "Network render waiting for results") | 
					
						
							|  |  |  | 			 | 
					
						
							| 
									
										
										
										
											2009-09-10 20:41:18 +00:00
										 |  |  | 			requestResult(conn, job_id, scene.current_frame) | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 			response = conn.getresponse() | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			if response.status == http.client.NO_CONTENT: | 
					
						
							| 
									
										
										
										
											2009-09-01 01:09:05 +00:00
										 |  |  | 				netsettings.job_id = clientSendJob(conn, scene) | 
					
						
							| 
									
										
										
										
											2009-09-10 20:41:18 +00:00
										 |  |  | 				requestResult(conn, job_id, scene.current_frame) | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 			 | 
					
						
							| 
									
										
										
										
											2009-09-01 01:09:05 +00:00
										 |  |  | 			while response.status == http.client.ACCEPTED and not self.test_break(): | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 				time.sleep(1) | 
					
						
							| 
									
										
										
										
											2009-09-10 20:41:18 +00:00
										 |  |  | 				requestResult(conn, job_id, scene.current_frame) | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 				response = conn.getresponse() | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 			if response.status != http.client.OK: | 
					
						
							|  |  |  | 				conn.close() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			r = scene.render_data | 
					
						
							|  |  |  | 			x= int(r.resolution_x*r.resolution_percentage*0.01) | 
					
						
							|  |  |  | 			y= int(r.resolution_y*r.resolution_percentage*0.01) | 
					
						
							|  |  |  | 			 | 
					
						
							| 
									
										
										
										
											2009-09-02 00:07:55 +00:00
										 |  |  | 			f = open(netsettings.path + "output.exr", "wb") | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 			buf = response.read(1024) | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			while buf: | 
					
						
							|  |  |  | 				f.write(buf) | 
					
						
							|  |  |  | 				buf = response.read(1024) | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			f.close() | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			result = self.begin_result(0, 0, x, y) | 
					
						
							| 
									
										
										
										
											2009-09-01 01:09:05 +00:00
										 |  |  | 			result.load_from_file(netsettings.path + "output.exr", 0, 0) | 
					
						
							| 
									
										
										
										
											2009-08-29 17:25:22 +00:00
										 |  |  | 			self.end_result(result) | 
					
						
							|  |  |  | 			 | 
					
						
							|  |  |  | 			conn.close() | 
					
						
							|  |  |  | 
 |