Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ data/assets/*
data.orig
data-
data+
local/*
local/*/
utilities/remote/bar
utilities/remote/foo.m
4 changes: 4 additions & 0 deletions utilities/escapeShellArg.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function s = escapeShellArg(p)
% Minimal shell-arg escaping for POSIX shells
s = ['"' strrep(p, '"', '\"') '"'];
end
13 changes: 13 additions & 0 deletions utilities/isSymlink.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function tf = isSymlink(p)
% Return true if p is a symbolic link (Unix/macOS).
% On Windows this returns false and we fall back to copyfile.
if ispc
tf = false;
return;
end
% test -L exits 0 if p is a symlink
[status, ~] = system(sprintf('test -L %s', escapeShellArg(p)));
tf = (status == 0);
end


15 changes: 14 additions & 1 deletion utilities/material/piMaterialText.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,20 @@

p.addRequired('material', @isstruct);
p.addRequired('thisR', @(x)(isa(x,'recipe')));
p.addParameter('remoterender', false);
p.addParameter('remoterender', true);
% AJ: set default to true.
% Reason:
% - If being rendered locally (remoterender = false),
% it assumes the texture map is in the recipe's output dir.
% If not present, block 82-97 handles copying the texture map to output dir.
% - However, if the db is in a centralised location (not output dir),
% in which case we use a symlink to point to the db (for complex scenes),
% the texture map is likely also in the db, so this check would fail.
% It throws a Normal Map not found warning.
% - In remote rendering, we bypass this check, assuming the texture map exists remotely.
% - So, when remoterender = true, we skip the block 82-97, which works for symlinked db.
% - Ideally, we should do the file existence check in the db location
% through the symlink.

p.parse(material, thisR, varargin{:});

Expand Down
4 changes: 3 additions & 1 deletion utilities/pbrt/parse/parseBlockMaterial.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
end
% Split the text line with ' "', '" ' and '"' to get key/val pair

thisLine = thisLine(~cellfun('isempty',thisLine));
% trim each token + remove empty/whitespace-only cells
thisLine = cellfun(@strtrim, thisLine, 'uni', false);
thisLine = thisLine(~cellfun(@(s) isempty(s) || all(isspace(s)), thisLine));

% deal with mix material
stringTypeIndex = find(contains(thisLine, 'string type'));
Expand Down
8 changes: 7 additions & 1 deletion utilities/pbrt/parse/parseGeometryText.m
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,13 @@
% subtree branches.
if ~isempty(subtrees)
rootAsset = piAssetCreate('type', 'branch');
rootAsset.name = 'root_B';
rootAsset.name = 'root_B'; % will be later renamed according to object

% AJ: Higher level transforms should be saved in the object node
rootAsset.scale = scale;
rootAsset.rotation = rotation;
rootAsset.translation = translation;

trees = tree(rootAsset);

% Graft each of the subtrees to the root node
Expand Down
71 changes: 26 additions & 45 deletions utilities/pbrt/parse/piParseObjectName.m
Original file line number Diff line number Diff line change
@@ -1,74 +1,55 @@
function [name, sz] = piParseObjectName(txt)
% Parse an ObjectName string in 'txt' to extract the object name and size.
%
% Cinema4D produces a line with #ObjectName in it. The format of the
% #ObjectName line appears to be something like this:
%
% #ObjectName Plane:Vector(5000, 0, 5000)
%
% The only cases we have seen are NAME:Vector(X,Z,Y). Someone seems to
% know the meaning of these three values which are read into 'res' below.
% The length is 2*X, width is 2*Y and height is 2*Z.
%
% Perhaps these numbers should always be treated as in meters or maybe
% centimeters? We need to figure this out. For the slantedBar scene we
% had the example above, and we think the scene might be about 100 meters,
% so this would make sense.
%
% We do not have a routine to fill in these values for non-Cinema4D
% objects.


% Find the location of one of these strings, such as #ObjectName
patternList = {'#ObjectName','#object name','#CollectionName',...
'#Instance MeshName','#MeshName','#Instance CollectionName','#Instance Parent'};

for ii = 1:numel(patternList)
pattern = patternList{ii};
loc = strfind(txt,pattern);
if isempty(loc)
continue;
else
if ~isempty(loc)
loc_dimension = strfind(txt,'#Dimension');
break;
end
end

% Look for a colon
% pos = strfind(txt,':');
if isempty(loc_dimension)
name = txt(loc(1)+length(pattern) + 1:end);
name = txt(loc(1)+length(pattern)+1:end);
sz.l = [];
sz.w = [];
sz.h = [];
else
name = txt(loc(1)+length(pattern) + 1:loc_dimension-1);
name = txt(loc(1)+length(pattern)+1:loc_dimension-1);

posA = strfind(txt,'[');
posB = strfind(txt,']');
res = sscanf(txt(posA(1)+1:posB(1)-1),'%f');
% Position minimima and maxima for lower left (X,Y), upper right.
sz.pmin = [-res(1)/2 -res(3)/2];
sz.pmax = [res(1)/2 res(3)/2];
raw = strtrim(txt(posA(1)+1:posB(1)-1));

% We are not really sure what these coordinates represent with respect to
% the scene or the camera direction. For one case we analyzed (a plane)
% this is what the values meant.
sz.l = res(1); % length (X)
sz.w = res(2); % depth (Z)
sz.h = res(3); % height (Y)
if isempty(raw)
% No numbers inside the brackets → return empty sizes
sz.l = [];
sz.w = [];
sz.h = [];
else
res = sscanf(raw,'%f');
if numel(res) < 3
% Not enough numbers → treat as empty
sz.l = [];
sz.w = [];
sz.h = [];
else
% Assign values
sz.pmin = [-res(1)/2 -res(3)/2];
sz.pmax = [ res(1)/2 res(3)/2];
sz.l = res(1);
sz.w = res(2);
sz.h = res(3);
end
end
end

% Remove quotes and spaces.
% Clean name
name = erase(name,'"');
name = erase(name,' ');

% Consider this:
%
% If we only have the identifier, then treat name as empty.
% if isequal(name,'_B') || isequal(name,'_L') || isequal(name,'_O')
% name = '';
% end


end
35 changes: 22 additions & 13 deletions utilities/piWrite.m
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,11 @@
if ~exist(workingDir,'dir')
mkdir(workingDir);
end
% Make a geometry directory
geometryDir = thisR.get('geometry dir');
if ~exist(geometryDir, 'dir'), mkdir(geometryDir); end

% AJ: geometry symlink is created in the write loop
% % Make a geometry directory
% geometryDir = thisR.get('geometry dir');
% if ~exist(geometryDir, 'dir'), mkdir(geometryDir); end

renderDir = thisR.get('rendered dir');
if ~exist(renderDir,'dir'), mkdir(renderDir); end
Expand Down Expand Up @@ -303,23 +305,30 @@ function piWriteCopy(thisR,overwriteresources,overwritepbrtfile, verbosity)
% Copy the spds and textures directory files.
status = status && copyfile(fullfile(sources(i).folder, sources(i).name), fullfile(outputDir,sources(i).name));
elseif overwriteresources
if sources(i).isdir && (strcmpi(sources(i).name,'spds') || strcmpi(sources(i).name,'textures') || strcmpi(sources(i).name,'instanced'))
% Copy the spds and textures directory files.
status = status && copyfile(fullfile(sources(i).folder, sources(i).name), fullfile(outputDir,sources(i).name));
else
% AJ: Lets use symlinks only. No copying
% if sources(i).isdir && (strcmpi(sources(i).name,'spds') || strcmpi(sources(i).name,'textures') || strcmpi(sources(i).name,'instanced'))
% % Copy the spds and textures directory files.
% status = status && copyfile(fullfile(sources(i).folder, sources(i).name), fullfile(outputDir,sources(i).name));
% else
% Selectively copy the files in the scene root folder
[~, ~, extension] = fileparts(sources(i).name);
% ChessSet needs input geometry because we can not parse it
% yet. --zhenyi
thisSrc = fullfile(sources(i).folder, sources(i).name);
thisDst = fullfile(outputDir, sources(i).name);
if ~(piContains(extension,'zip') || piContains(extension,'json'))
thisFile = fullfile(sources(i).folder, sources(i).name);
if verbosity > 1
fprintf('Copying %s\n',thisFile)
if verbosity > 1, fprintf('Copying %s\n', thisSrc); end
if isSymlink(thisSrc)
% Recreate symlink in destination
[~, tgt] = system(sprintf('readlink %s', escapeShellArg(thisSrc)));
tgt = strtrim(tgt);
status = status && (system(sprintf('ln -sfn %s %s', ...
escapeShellArg(tgt), escapeShellArg(thisDst))) == 0);
else
status = status && copyfile(thisSrc, thisDst);
end
status = status && copyfile(thisFile, fullfile(outputDir,sources(i).name));
%status = status && system(sprintf('cp -r %s %s \n',thisFile, fullfile(outputDir,sources(i).name)));
end
end
% end
end
end
if(~status)
Expand Down
3 changes: 2 additions & 1 deletion utilities/recipe/piRecipeDefault.m
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,8 @@
fname = fullfile(sceneDir,sceneFile);
elseif ~strcmpi(fname, which(fname))
% Ignoring case. Might be a bad idea (BW).
error('File exists on your path, but not where expected.')
% AJ: throws error when pre-downloaded; commented out
% error('File exists on your path, but not where expected.')
end

%% If we are here, we found the file. So create the recipe.
Expand Down
4 changes: 3 additions & 1 deletion utilities/texture/piTextureCreate.m
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,10 @@
texture.v2.type = 'vector3';
texture.v2.value = [];

% AJ: don't hardcode invert to false
texture.invert.type = 'bool';
texture.invert.value = 'false';
texture.invert.value = [];
% texture.invert.value = 'false';
case 'checkerboard'
texture.type = 'checkerboard';

Expand Down
40 changes: 25 additions & 15 deletions utilities/texture/piTextureFileFormat.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@
texSlotName = textureList{ii}.filename.value;
thisImgPath = fullfile(inputDir, texSlotName);

if ~exist(thisImgPath,'file')
% It could be the material presets
thisImgPath = which(texSlotName);
end
% % AJ: textures are symlinked, can't find on local path
% if ~exist(thisImgPath,'file')
% % It could be the material presets
% thisImgPath = which(texSlotName);
% end

if isempty(find(strcmp(ext, {'.png','.PNG','.exr','.jpg'}),1))
if exist(thisImgPath, 'file')
Expand Down Expand Up @@ -76,11 +77,17 @@
if contains(textureList{ii}.name,{'tex_'}) && ...
exist(fullfile(inputDir, texSlotName),'file') && ...
contains(textureList{ii}.name,{'.alphamap.'})

outputFile = fullfile(path,[name,'_alphamap.png']);
outputPath = fullfile(inputDir, outputFile);
[img, ~, alphaImage] = imread(thisImgPath);


% AJ: don't need to append _alphamap?
% for instance, a name was 'tlusfbvia_4K_Opacity_alphamap'
% outputFile = fullfile(path,[name,'_alphamap.png']);
outputFile = fullfile(path,[name, '.png']);
% AJ: the below hardcoding is due to local vs remote render setups
% Replace with your own texture directory as needed
texturesDir = '/acorn/data/iset/Resources/';
outputPath = fullfile(texturesDir, outputFile);
[img, ~, alphaImage] = imread(outputPath);

if size(img,3)~=1 && isempty(alphaImage) && ~isempty(find(img(:,:,1) ~= img(:,:,2), 1))
disp('No alpha texture map is available.');
return;
Expand Down Expand Up @@ -119,16 +126,19 @@
normalImgPath = thisMat.normalmap.value;
thisMat.normalmap.type = 'string';
thisImgPath = fullfile(inputDir, normalImgPath);

if ~exist(thisImgPath,'file')
% It could be the material presets
thisImgPath = which(normalImgPath);
end

% % AJ: textures are symlinked, can't find on local path
% if ~exist(thisImgPath,'file')
% % It could be the material presets
% thisImgPath = which(normalImgPath);
% end
if isempty(normalImgPath)
continue;
end

if exist(thisImgPath, 'file') && ~isempty(normalImgPath)
% % AJ: assume file exists (it doesn't locally; symlinked)
% if exist(thisImgPath, 'file') && ~isempty(normalImgPath)
if ~isempty(normalImgPath)

[path, name, ext] = fileparts(pathToLinux(normalImgPath));
if strcmp(ext, '.exr') || strcmp(ext, '.png') || strcmp(ext, '.jpg')
Expand Down
14 changes: 8 additions & 6 deletions utilities/texture/piTextureText.m
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@
% in the base or in textures/*. If it does, we do not
% need to copy it.

if ~isempty(getpref('ISETDocker','remoteHost')) && thisR.useDB
remoteSceneDir = getpref('ISETDocker','remoteSceneDir');
texturePath = fullfile(remoteSceneDir,texturePath);
end
% AJ: we are using symlinks, no absolute path needed
% if ~isempty(getpref('ISETDocker','remoteHost')) && thisR.useDB
% remoteSceneDir = getpref('ISETDocker','remoteSceneDir');
% texturePath = fullfile(remoteSceneDir,texturePath);
% end

if exist(fullfile(oDir,thisVal),'file')
% If the file is in the root of the scene, move it
Expand Down Expand Up @@ -157,8 +158,9 @@
if ~isempty(getpref('ISETDocker','remoteHost'))&& thisR.useDB ...
&& ~strncmpi(thisVal,'/',1)
% We trust that the texture will be there on the server
remoteFolder = fileparts(thisR.inputFile);
imgFile = fullfile(remoteFolder,'textures',thisVal);
% remoteFolder = fileparts(thisR.inputFile); % BUG
% AJ: writes full remote path
imgFile = fullfile('textures',thisVal);
thisText = sprintf(' "%s %s" "%s" ',...
thisType, textureParams{ii}, imgFile);
else
Expand Down