Commit 4e6966e0 authored by Andrey Filippov's avatar Andrey Filippov

Debugging gltf export

parent 82b27573
......@@ -27,6 +27,18 @@
<description>A Maven project implementing imagej-elphel plugin</description>
<dependencies>
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1</version>
</dependency>
<!-- ssh support - see https://www.baeldung.com/java-ssh-connection -->
<!-- https://mvnrepository.com/artifact/org.apache.sshd/sshd-core -->
<dependency>
......
......@@ -383,18 +383,18 @@ public class CLTParameters {
public boolean photo_debug = false; // Generate images and text
public double tex_disp_adiffo = 0.35; // 0.3; disparity absolute tolerance to connect in ortho directions
public double tex_disp_rdiffo = 0.12; // 0.1; disparity relative tolerance to connect in ortho directions
public double tex_disp_adiffd = 0.6; // 0.4; disparity absolute tolerance to connect in diagonal directions
public double tex_disp_rdiffd = 0.18; // 0.12; disparity relative tolerance to connect in diagonal directions
public double tex_disp_adiffo = 0.12; // 0.35; // 0.3; disparity absolute tolerance to connect in ortho directions
public double tex_disp_rdiffo = 0.1; // 0.12; // 0.1; disparity relative tolerance to connect in ortho directions
public double tex_disp_adiffd = 0.18; // 0.6; // 0.4; disparity absolute tolerance to connect in diagonal directions
public double tex_disp_rdiffd = 0.15; // 0.18; // 0.12; disparity relative tolerance to connect in diagonal directions
public double tex_disp_fof = 1.5; // Increase tolerance for friend of a friend
public double tex_fg_bg = 0.1; // Minimal FG/BG disparity difference (NaN bg if difference from FG < this)
public double tex_distort = 0.8; // Maximal texture distortion to accumulate multiple scenes (0 - any)
public double tex_mb = 1.0; // Reduce texture weight if motion blur exceeds this (as square of MB length)
public boolean tex_um = true; // Use unsharp mask filter for textures
public double tex_um_sigma = 10.0; // Unsharp mask sigma for textures
public double tex_um_weight = 0.97;// Unsharp mask weight
public double tex_um_sigma = 2.0; // Unsharp mask sigma for textures
public double tex_um_weight = 0.8;// Unsharp mask weight
public boolean tex_lwir_autorange = true; // Autorange LWIR textures
public boolean tex_um_fixed = false; // Use fixed range after unsharp mask instead of autorange
public double tex_um_range = 500; // Full range after unsharp mask
......@@ -403,8 +403,25 @@ public class CLTParameters {
public int tex_hist_bins = 1024; // Number of histogram bins to use for texture histograms
public int tex_hist_segments = 32; // Number of evenly-spaced percentiles to use for histogram normalization
public boolean tex_color = true; // Use pseudo-colored textures
public int tex_palette = 2; // Palette number for pseudo colors
public int tex_palette = 1; // Palette number for pseudo colors
public int max_clusters = 5000; // Maximal number of clusters to generate for one run
public boolean remove_scans = true; // Remove all unneeded scans when generating x3d output to save memory
public boolean output_x3d = true; // Generate x3d output
public boolean output_obj = true; // Generate Wavefront obj output
public boolean output_glTF = true; // Generate glTF output
public boolean correct_distortions = true; // Correct lens geometric distortions in a model (will need backdrop to be corrected too)
public boolean show_triangles = false; // true; // Show generated triangles
public boolean avg_cluster_disp = false; // Weight-average disparity for the whole cluster
public double maxDispTriangle = 0.0; // 0.2; // Maximal relative disparity difference in a triangle face
public double maxZtoXY = 0.0; // 30.0; // world dz /sqrt(dx*dx + dz*dz), // 10.0. <=0 - do not use
public double maxZ = 20000; // maximal distance to far object
public boolean limitZ = true; // limit Z, if false - remove triangle
public double infinityDistance = 10000; // Distance to generate backdrop (0 - use regular backdrop)
public int min_bgnd_tiles = 10; // Minimal number of background tiles to generate background
public boolean gltf_emissive = false; // true; // Use emissive textures
public boolean show_textures = true; // show generated textures
public boolean debug_filters = false;// show intermediate results of filtering
// not used anywhere so far
......@@ -472,18 +489,7 @@ public class CLTParameters {
public double poles_min_strength = 0.1; // Set new pole segment strength to max of horizontal correlation and this value
public boolean poles_force_disp = true; // Set disparity to that of the bottom of existing segment (false - use hor. disparity)
public int max_clusters = 500; // Maximal number of clusters to generate for one run
public boolean remove_scans = true; // Remove all unneeded scans when generating x3d output to save memory
public boolean output_x3d = true; // Generate x3d output
public boolean output_obj = true; // Generate Wavefront obj output
public boolean correct_distortions = false; // Correct lens geometric distortions in a model (will need backdrop to be corrected too)
public boolean show_triangles = true; // Show generated triangles
public boolean avg_cluster_disp = false; // Weight-average disparity for the whole cluster
public double maxDispTriangle = 0.2; // Maximal relative disparity difference in a triangle face
public double infinityDistance = 10000; // Distance to generate backdrop (0 - use regular backdrop)
public int min_bgnd_tiles = 10; // Minimal number of background tiles to generate background
public boolean shUseFlaps = true; // Split into shells with flaps
public boolean shAggrFade = false; // true; // Aggressive fade alpha (whole boundary)
public int shMinArea = 1; // Minimal shell area (not counting flaps
......@@ -1420,6 +1426,25 @@ public class CLTParameters {
properties.setProperty(prefix+"tex_color", this.tex_color+""); // boolean
properties.setProperty(prefix+"tex_palette", this.tex_palette+""); // int
properties.setProperty(prefix+"max_clusters", this.max_clusters+"");
properties.setProperty(prefix+"remove_scans", this.remove_scans+"");
properties.setProperty(prefix+"output_x3d", this.output_x3d+"");
properties.setProperty(prefix+"output_obj", this.output_obj+"");
properties.setProperty(prefix+"output_glTF", this.output_glTF+"");
properties.setProperty(prefix+"correct_distortions", this.correct_distortions+"");
properties.setProperty(prefix+"show_triangles", this.show_triangles+"");
properties.setProperty(prefix+"avg_cluster_disp", this.avg_cluster_disp+"");
properties.setProperty(prefix+"maxDispTriangle", this.maxDispTriangle +"");
properties.setProperty(prefix+"maxZtoXY", this.maxZtoXY +"");
properties.setProperty(prefix+"maxZ", this.maxZ +"");
properties.setProperty(prefix+"limitZ", this.limitZ+"");
properties.setProperty(prefix+"infinityDistance", this.infinityDistance +"");
properties.setProperty(prefix+"min_bgnd_tiles", this.min_bgnd_tiles+"");
properties.setProperty(prefix+"gltf_emissive", this.gltf_emissive+"");
properties.setProperty(prefix+"show_textures", this.show_textures+"");
properties.setProperty(prefix+"debug_filters", this.debug_filters+"");
......@@ -1485,18 +1510,6 @@ public class CLTParameters {
properties.setProperty(prefix+"poles_min_strength", this.poles_min_strength +"");
properties.setProperty(prefix+"or_maxDisp", this.or_maxDisp+"");
properties.setProperty(prefix+"max_clusters", this.max_clusters+"");
properties.setProperty(prefix+"remove_scans", this.remove_scans+"");
properties.setProperty(prefix+"output_x3d", this.output_x3d+"");
properties.setProperty(prefix+"output_obj", this.output_obj+"");
properties.setProperty(prefix+"correct_distortions", this.correct_distortions+"");
properties.setProperty(prefix+"show_triangles", this.show_triangles+"");
properties.setProperty(prefix+"avg_cluster_disp", this.avg_cluster_disp+"");
properties.setProperty(prefix+"maxDispTriangle", this.maxDispTriangle +"");
properties.setProperty(prefix+"infinityDistance", this.infinityDistance +"");
properties.setProperty(prefix+"min_bgnd_tiles", this.min_bgnd_tiles+"");
properties.setProperty(prefix+"shUseFlaps", this.shUseFlaps+"");
properties.setProperty(prefix+"shAggrFade", this.shAggrFade+"");
properties.setProperty(prefix+"shMinArea", this.shMinArea+"");
......@@ -2312,7 +2325,26 @@ public class CLTParameters {
if (properties.getProperty(prefix+"tex_hist_segments")!=null) this.tex_hist_segments=Integer.parseInt(properties.getProperty(prefix+"tex_hist_segments"));
if (properties.getProperty(prefix+"tex_color")!=null) this.tex_color=Boolean.parseBoolean(properties.getProperty(prefix+"tex_color"));
if (properties.getProperty(prefix+"tex_palette")!=null) this.tex_palette=Integer.parseInt(properties.getProperty(prefix+"tex_palette"));
if (properties.getProperty(prefix+"max_clusters")!=null) this.max_clusters=Integer.parseInt(properties.getProperty(prefix+"max_clusters"));
if (properties.getProperty(prefix+"remove_scans")!=null) this.remove_scans=Boolean.parseBoolean(properties.getProperty(prefix+"remove_scans"));
if (properties.getProperty(prefix+"output_x3d")!=null) this.output_x3d=Boolean.parseBoolean(properties.getProperty(prefix+"output_x3d"));
if (properties.getProperty(prefix+"output_obj")!=null) this.output_obj=Boolean.parseBoolean(properties.getProperty(prefix+"output_obj"));
if (properties.getProperty(prefix+"output_glTF")!=null) this.output_glTF=Boolean.parseBoolean(properties.getProperty(prefix+"output_glTF"));
if (properties.getProperty(prefix+"correct_distortions")!=null) this.correct_distortions=Boolean.parseBoolean(properties.getProperty(prefix+"correct_distortions"));
if (properties.getProperty(prefix+"show_triangles")!=null) this.show_triangles=Boolean.parseBoolean(properties.getProperty(prefix+"show_triangles"));
if (properties.getProperty(prefix+"avg_cluster_disp")!=null) this.avg_cluster_disp=Boolean.parseBoolean(properties.getProperty(prefix+"avg_cluster_disp"));
if (properties.getProperty(prefix+"maxDispTriangle")!=null) this.maxDispTriangle=Double.parseDouble(properties.getProperty(prefix+"maxDispTriangle"));
if (properties.getProperty(prefix+"maxZtoXY")!=null) this.maxZtoXY=Double.parseDouble(properties.getProperty(prefix+"maxZtoXY"));
if (properties.getProperty(prefix+"maxZ")!=null) this.maxZ=Double.parseDouble(properties.getProperty(prefix+"maxZ"));
if (properties.getProperty(prefix+"limitZ")!=null) this.limitZ=Boolean.parseBoolean(properties.getProperty(prefix+"limitZ"));
if (properties.getProperty(prefix+"infinityDistance")!=null) this.infinityDistance=Double.parseDouble(properties.getProperty(prefix+"infinityDistance"));
if (properties.getProperty(prefix+"min_bgnd_tiles")!=null) this.min_bgnd_tiles=Integer.parseInt(properties.getProperty(prefix+"min_bgnd_tiles"));
if (properties.getProperty(prefix+"gltf_emissive")!=null) this.gltf_emissive=Boolean.parseBoolean(properties.getProperty(prefix+"gltf_emissive"));
if (properties.getProperty(prefix+"show_textures")!=null) this.show_textures=Boolean.parseBoolean(properties.getProperty(prefix+"show_textures"));
if (properties.getProperty(prefix+"debug_filters")!=null) this.debug_filters=Boolean.parseBoolean(properties.getProperty(prefix+"debug_filters"));
......@@ -2378,16 +2410,6 @@ public class CLTParameters {
if (properties.getProperty(prefix+"poles_min_strength")!=null) this.poles_min_strength=Double.parseDouble(properties.getProperty(prefix+"poles_min_strength"));
if (properties.getProperty(prefix+"poles_force_disp")!=null) this.poles_force_disp=Boolean.parseBoolean(properties.getProperty(prefix+"poles_force_disp"));
if (properties.getProperty(prefix+"max_clusters")!=null) this.max_clusters=Integer.parseInt(properties.getProperty(prefix+"max_clusters"));
if (properties.getProperty(prefix+"remove_scans")!=null) this.remove_scans=Boolean.parseBoolean(properties.getProperty(prefix+"remove_scans"));
if (properties.getProperty(prefix+"output_x3d")!=null) this.output_x3d=Boolean.parseBoolean(properties.getProperty(prefix+"output_x3d"));
if (properties.getProperty(prefix+"output_obj")!=null) this.output_obj=Boolean.parseBoolean(properties.getProperty(prefix+"output_obj"));
if (properties.getProperty(prefix+"correct_distortions")!=null) this.correct_distortions=Boolean.parseBoolean(properties.getProperty(prefix+"correct_distortions"));
if (properties.getProperty(prefix+"show_triangles")!=null) this.show_triangles=Boolean.parseBoolean(properties.getProperty(prefix+"show_triangles"));
if (properties.getProperty(prefix+"avg_cluster_disp")!=null) this.avg_cluster_disp=Boolean.parseBoolean(properties.getProperty(prefix+"avg_cluster_disp"));
if (properties.getProperty(prefix+"maxDispTriangle")!=null) this.maxDispTriangle=Double.parseDouble(properties.getProperty(prefix+"maxDispTriangle"));
if (properties.getProperty(prefix+"infinityDistance")!=null) this.infinityDistance=Double.parseDouble(properties.getProperty(prefix+"infinityDistance"));
if (properties.getProperty(prefix+"min_bgnd_tiles")!=null) this.min_bgnd_tiles=Integer.parseInt(properties.getProperty(prefix+"min_bgnd_tiles"));
if (properties.getProperty(prefix+"shUseFlaps")!=null) this.shUseFlaps=Boolean.parseBoolean(properties.getProperty(prefix+"shUseFlaps"));
if (properties.getProperty(prefix+"shAggrFade")!=null) this.shAggrFade=Boolean.parseBoolean(properties.getProperty(prefix+"shAggrFade"));
if (properties.getProperty(prefix+"shMinArea")!=null) this.shMinArea=Integer.parseInt(properties.getProperty(prefix+"shMinArea"));
......@@ -3352,6 +3374,7 @@ public class CLTParameters {
"Maximal texture distortion to accumulate multiple scenes (neighbor tile center offset from the uniform grid. 0 - do not filter");
gd.addNumericField("Maximal motion blur to reduce weight",this.tex_mb, 5,7,"pix",
"Reduce texture weight if motion blur exceeds this (as square of MB length).");
gd.addMessage ("Textures rendering");
gd.addCheckbox ("Apply unsharp mask", this.tex_um,
"Use unsharp mask filter for texture.");
......@@ -3377,6 +3400,41 @@ public class CLTParameters {
"Use pseudo-colored textures (false - monochrome, float).");
gd.addNumericField("Palette number", this.tex_palette, 0,3,"",
"Palette number for pseudo colors.");
gd.addMessage ("Triangulation");
gd.addNumericField("Maximal output meshes", this.max_clusters, 0,3,"",
"Maximal number of output meshes to generate.");
gd.addCheckbox ("Remove all unneeded scans before x3d", this.remove_scans,
"Remove all unneeded scans when generating x3d output to save memory (old).");
gd.addCheckbox ("Generate x3d", this.output_x3d,
"Generate x3d output.");
gd.addCheckbox ("Generate Wavefront obj", this.output_obj,
"Generate Wavefront obj output.");
gd.addCheckbox ("Generate glTF output", this.output_glTF,
"Generate glTF output.");
gd.addCheckbox ("Correct lens distortions in a model", this.correct_distortions,
"Correct lens geometric distortions in a model (will need backdrop to be corrected too)");
gd.addCheckbox ("Show triangles", this.show_triangles,
"Show generated triangles.");
gd.addCheckbox ("Same cluster disparity", this.avg_cluster_disp,
"Weight-average disparity for the whole cluster.");
gd.addNumericField("Maximal relative disparity difference",this.maxDispTriangle, 4, 6, "",
"Maximal relative disparity difference in a triangle face to show.");
gd.addNumericField("Maximal world dz to dx,dy", this.maxZtoXY, 4, 6, "",
"Maximal world dz to dx,dy of a triangular face to show");
gd.addNumericField("Maximal world Z", this.maxZ, 4, 6, "m",
"Maximal absolute distance.");
gd.addCheckbox ("Clamp Z", this.limitZ,
"Replace too far distance with the maximal one. If false - remove triangle faces that havce too far vertices.");
gd.addNumericField("Distance to a backdrop", this.infinityDistance,8,8,"m",
"Distance to generate backdrop (0 - use regular backdrop).");
gd.addNumericField("Minimal background tiles", this.min_bgnd_tiles, 0,4,"",
"Minimal number of background tiles to generate background.");
gd.addMessage ("glTF export");
gd.addCheckbox ("glTF use emissive textures", this.gltf_emissive);
gd.addMessage ("Earlier 3D generation parameters");
gd.addCheckbox ("Show generated textures", this.show_textures);
......@@ -3456,16 +3514,6 @@ public class CLTParameters {
gd.addNumericField("Set new pole segment strength to max of horizontal correlation and this value", this.poles_min_strength, 3);
gd.addCheckbox ("Set disparity to that of the bottom of existing segment (false - use hor. disparity)", this.poles_force_disp);
gd.addNumericField("Maximal number of output meshes to generate", this.max_clusters, 0);
gd.addCheckbox ("Remove all unneeded scans when generating x3d output to save memory", this.remove_scans);
gd.addCheckbox ("Generate x3d output", this.output_x3d);
gd.addCheckbox ("Generate Wavefront obj output", this.output_obj);
gd.addCheckbox ("Correct lens geometric distortions in a model (will need backdrop to be corrected too)", this.correct_distortions);
gd.addCheckbox ("Show generated triangles", this.show_triangles);
gd.addCheckbox ("Weight-average disparity for the whole cluster ", this.avg_cluster_disp);
gd.addNumericField("Maximal disparity difference in a triangle face to show", this.maxDispTriangle, 6);
gd.addNumericField("Distance to generate backdrop (0 - use regular backdrop)", this.infinityDistance, 8);
gd.addNumericField(" Minimal number of background tiles to generate background", this.min_bgnd_tiles, 0);
gd.addCheckbox ("Split into shells with flaps", this.shUseFlaps);
gd.addCheckbox ("Aggressive fade alpha (whole boundary)", this.shAggrFade);
......@@ -4407,6 +4455,25 @@ public class CLTParameters {
this.tex_color = gd.getNextBoolean();
this.tex_palette = (int) gd.getNextNumber();
this.max_clusters= (int) gd.getNextNumber();
this.remove_scans= gd.getNextBoolean();
this.output_x3d= gd.getNextBoolean();
this.output_obj= gd.getNextBoolean();
this.output_glTF= gd.getNextBoolean();
this.correct_distortions= gd.getNextBoolean();
this.show_triangles= gd.getNextBoolean();
this.avg_cluster_disp= gd.getNextBoolean();
this.maxDispTriangle= gd.getNextNumber();
this.maxZtoXY= gd.getNextNumber();
this.maxZ= gd.getNextNumber();
this.limitZ= gd.getNextBoolean();
this.infinityDistance= gd.getNextNumber();
this.min_bgnd_tiles= (int) gd.getNextNumber();
this.gltf_emissive= gd.getNextBoolean();
this.show_textures= gd.getNextBoolean();
this.debug_filters= gd.getNextBoolean();
this.min_smth= gd.getNextNumber();
......@@ -4471,16 +4538,6 @@ public class CLTParameters {
this.poles_min_strength= gd.getNextNumber();
this.poles_force_disp= gd.getNextBoolean();
this.max_clusters= (int) gd.getNextNumber();
this.remove_scans= gd.getNextBoolean();
this.output_x3d= gd.getNextBoolean();
this.output_obj= gd.getNextBoolean();
this.correct_distortions= gd.getNextBoolean();
this.show_triangles= gd.getNextBoolean();
this.avg_cluster_disp= gd.getNextBoolean();
this.maxDispTriangle= gd.getNextNumber();
this.infinityDistance= gd.getNextNumber();
this.min_bgnd_tiles= (int) gd.getNextNumber();
this.shUseFlaps= gd.getNextBoolean();
this.shAggrFade= gd.getNextBoolean();
this.shMinArea= (int) gd.getNextNumber();
......
......@@ -903,7 +903,7 @@ public class OpticalFlow {
* @param debug_level if > 0; print number of tiles to correlate
* @return flowXY vectors only for tiles to be updated or null if no tiles left
*/
double [][] recalculateFlowXY(
static double [][] recalculateFlowXY(
final double [][] flowXY, // will update
final double [][] flowXY_prev, // previous flowXY (may be null for tiles)
final double [][] corr_vectorsXY,
......
......@@ -65,6 +65,7 @@ import com.elphel.imagej.correction.EyesisCorrections;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.readers.ImagejJp4Tiff;
import com.elphel.imagej.x3d.export.TriMesh;
import com.elphel.imagej.x3d.export.WavefrontExport;
import com.elphel.imagej.x3d.export.X3dOutput;
......@@ -7552,11 +7553,11 @@ public class QuadCLTCPU {
double [] rel_lim = {
getMarginFromHist(
hist, // histogram
too_cold, // double cumul_val, // cummulative number of items to be ignored
too_cold, // double cumul_val, // cumulative number of items to be ignored
false), // boolean high_marg)
getMarginFromHist(
hist, // histogram
too_hot, // double cumul_val, // cummulative number of items to be ignored
too_hot, // double cumul_val, // cumulative number of items to be ignored
true)}; // boolean high_marg)
double [] abs_lim = {
rel_lim[0] * (hard_hot - hard_cold) + hard_cold,
......@@ -12455,6 +12456,7 @@ public class QuadCLTCPU {
double infinity_disparity = geometryCorrection.getDisparityFromZ(clt_parameters.infinityDistance);
X3dOutput x3dOutput = null;
WavefrontExport wfOutput = null;
ArrayList<TriMesh> tri_meshes = null;
if (clt_parameters.remove_scans){
System.out.println("Removing all scans but the first(background) and the last to save memory");
System.out.println("Will need to re-start the program to be able to output differently");
......@@ -12489,20 +12491,24 @@ public class QuadCLTCPU {
}
if (clt_parameters.output_obj && (x3d_path != null)) {
try {
wfOutput = new WavefrontExport(
wfOutput = new WavefrontExport(
x3d_path,
correctionsParameters.getModelName(this.image_name),
clt_parameters,
correctionsParameters,
geometryCorrection,
tp.clt_3d_passes);
} catch (IOException e) {
System.out.println("Failed to open Wavefront files for writing");
// TODO Auto-generated catch block
e.printStackTrace();
// do nothing, just keep
}
} catch (IOException e) {
System.out.println("Failed to open Wavefront files for writing");
// TODO Auto-generated catch block
e.printStackTrace();
// do nothing, just keep
}
}
if (clt_parameters.output_glTF && (x3d_path != null)) {
tri_meshes = new ArrayList<TriMesh>();
}
if (x3dOutput != null) {
x3dOutput.generateBackground(clt_parameters.infinityDistance <= 0.0); // needs just first (background) scan
}
......@@ -12579,6 +12585,7 @@ public class QuadCLTCPU {
generateClusterX3d(
x3dOutput,
wfOutput, // output WSavefront if not null
tri_meshes, // ArrayList<TriMesh> tri_meshes,
texturePath,
"INFINITY", // id (scanIndex - next_pass), // id
"INFINITY", // class
......@@ -12590,7 +12597,11 @@ public class QuadCLTCPU {
showTri, // (scanIndex < next_pass + 1) && clt_parameters.show_triangles,
infinity_disparity, // 0.3
clt_parameters.grow_disp_max, // other_range, // 2.0 'other_range - difference from the specified (*_CM)
clt_parameters.maxDispTriangle);
clt_parameters.maxDispTriangle,
clt_parameters.maxZtoXY, // double maxZtoXY, // 10.0. <=0 - do not use
clt_parameters.maxZ,
clt_parameters.limitZ,
debugLevel + 1); // int debug_level) > 0
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
......@@ -12668,11 +12679,11 @@ public class QuadCLTCPU {
}
}
boolean showTri = !batch_mode && (debugLevel > -1) && (((scanIndex < next_pass + 1) && clt_parameters.show_triangles) ||((scanIndex - next_pass) == 73));
try {
generateClusterX3d(
generateClusterX3d(
x3dOutput,
wfOutput, // output WSavefront if not null
tri_meshes, // ArrayList<TriMesh> tri_meshes,
texturePath,
"shape_id-"+(scanIndex - next_pass), // id
null, // class
......@@ -12685,7 +12696,11 @@ public class QuadCLTCPU {
// FIXME: make a separate parameter:
infinity_disparity, // 0.25 * clt_parameters.bgnd_range, // 0.3
clt_parameters.grow_disp_max, // other_range, // 2.0 'other_range - difference from the specified (*_CM)
clt_parameters.maxDispTriangle);
clt_parameters.maxDispTriangle,
clt_parameters.maxZtoXY, // double maxZtoXY, // 10.0. <=0 - do not use
clt_parameters.maxZ,
clt_parameters.limitZ,
debugLevel + 1); // int debug_level) > 0
} catch (IOException e) {
e.printStackTrace();
return false;
......@@ -12718,6 +12733,7 @@ public class QuadCLTCPU {
public void generateClusterX3d( // USED in lwir
X3dOutput x3dOutput, // output x3d if not null
WavefrontExport wfOutput, // output WSavefront if not null
ArrayList<TriMesh> tri_meshes,
String texturePath,
String id,
String class_name,
......@@ -12729,18 +12745,23 @@ public class QuadCLTCPU {
boolean show_triangles,
double min_disparity,
double max_disparity,
double maxDispTriangle
double maxDispTriangle, // relative <=0 - do not use
double maxZtoXY, // 10.0. <=0 - do not use
double maxZ, // far clip (0 - do not clip). Negative - limit by max
boolean limitZ,
int debug_level
) throws IOException
{
// int debug_level = 1;
if (bounds == null) {
return; // not used in lwir
}
int [][] indices = tp.getCoordIndices( // starting with 0, -1 - not selected
int [][] indices = tp.getCoordIndices( // starting with 0, -1 - not selected // updated 09.18.2022
bounds,
selected);
double [][] texCoord = TileProcessor.getTexCoords( // get texture coordinates for indices
indices);
double [][] worldXYZ = tp.getCoords( // get world XYZ in meters for indices
double [][] worldXYZ = tp.getCoords( // get world XYZ in meters for indices // updated 09.18.2022
disparity,
min_disparity,
max_disparity,
......@@ -12750,7 +12771,7 @@ public class QuadCLTCPU {
correctDistortions, // requires backdrop image to be corrected also
this.geometryCorrection);
double [] indexedDisparity = tp.getIndexedDisparities( // get disparity for each index
double [] indexedDisparity = tp.getIndexedDisparities( // get disparity for each index // updated 09.18.2022
disparity,
min_disparity,
max_disparity,
......@@ -12761,12 +12782,83 @@ public class QuadCLTCPU {
int [][] triangles = TileProcessor.triangulateCluster(
indices);
triangles = TileProcessor.filterTriangles( // remove crazy triangles with large disparity difference
triangles,
indexedDisparity, // disparities per vertex index
maxDispTriangle); // maximal disparity difference in a triangle
// double maxZtoXY = 10.0; // maximal delta_z to sqrt(dx^23 + dy^2)
int num_removed = 0;
if (maxDispTriangle > 0.0) {
int pre_num = triangles.length;
triangles = TileProcessor.filterTriangles( // remove crazy triangles with large disparity difference
triangles,
indexedDisparity, // disparities per vertex index
maxDispTriangle, // maximal disparity difference in a triangle
debug_level + 0); // int debug_level);
if (triangles.length < pre_num) {
num_removed += pre_num - triangles.length;
if (debug_level > 0) {
System.out.println("filterTriangles() removed "+ (pre_num - triangles.length)+" triangles");
}
}
}
if ((maxZ != 0.0) && limitZ) {
for (int i = 0; i < worldXYZ.length; i++) {
if (worldXYZ[i][2] < -maxZ) {
double k = -maxZ/worldXYZ[i][2];
worldXYZ[i][0] *= k;
worldXYZ[i][1] *= k;
worldXYZ[i][2] *= k;
}
}
}
if ((maxZtoXY > 0.0) || ((maxZ != 0) && !limitZ) ) {
int pre_num = triangles.length;
triangles = TileProcessor.filterTrianglesWorld(
triangles,
worldXYZ, // world per vertex index
maxZtoXY,
maxZ);
if (triangles.length < pre_num) {
num_removed += pre_num - triangles.length;
if (debug_level > 0) {
System.out.println("filterTrianglesWorld() removed "+ (pre_num - triangles.length)+" triangles");
}
}
}
if (triangles.length == 0) {
if (debug_level > 0) {
System.out.println("generateClusterX3d() no triangles left in a cluster");
}
return; // all triangles removed
}
if (num_removed > 0) {
int [] re_index = TileProcessor.reIndex( // Move to TriMesh?
indices, // will be modified if needed (if some indices are removed
triangles);
if (re_index != null) {// need to update other arrays: texCoord, worldXYZ. indexedDisparity[] will not be used
int num_indices_old = worldXYZ.length;
int [] inv_index = new int [num_indices_old];
Arrays.fill(inv_index,-1); // just to get an error
for (int i = 0; i < re_index.length; i++) {
inv_index[re_index[i]] = i;
}
double [][] texCoord_new = new double [re_index.length][];
double [][] worldXYZ_new = new double [re_index.length][];
for (int i = 0; i < re_index.length; i++) {
texCoord_new[i] = texCoord[re_index[i]];
worldXYZ_new[i] = worldXYZ[re_index[i]];
}
texCoord = texCoord_new;
worldXYZ = worldXYZ_new;
for (int i = 0; i < triangles.length; i++) {
for (int j=0; j < triangles[i].length; j++) {
triangles[i][j] = inv_index[triangles[i][j]];
}
}
}
if (debug_level > 0) {
show_triangles = true; // show after removed
}
}
if (show_triangles) {
double [] ddisp = (disparity == null)?(new double[1]):disparity;
......@@ -12800,6 +12892,13 @@ public class QuadCLTCPU {
worldXYZ,
triangles);
}
if (tri_meshes != null) {
tri_meshes.add(new TriMesh(
texturePath, // String texture_image,
worldXYZ, // double [][] worldXYZ,
texCoord, // double [][] texCoord,
triangles)); // int [][] triangles
}
}
......
......@@ -28,6 +28,8 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import org.json.JSONException;
import com.elphel.imagej.cameras.CLTParameters;
import com.elphel.imagej.cameras.ColorProcParameters;
import com.elphel.imagej.cameras.EyesisCorrectionParameters;
......@@ -35,6 +37,8 @@ import com.elphel.imagej.common.ShowDoubleFloatArrays;
import com.elphel.imagej.correction.EyesisCorrections;
import com.elphel.imagej.gpu.GpuQuad;
import com.elphel.imagej.gpu.TpTask;
import com.elphel.imagej.x3d.export.GlTfExport;
import com.elphel.imagej.x3d.export.TriMesh;
import com.elphel.imagej.x3d.export.WavefrontExport;
import com.elphel.imagej.x3d.export.X3dOutput;
......@@ -92,7 +96,7 @@ public class TexturedModel {
}
}
final int dbg_tile = (debugLevel>0)? 2021:-1; // 977 : -1;
final int dbg_tile = (debugLevel>0)? 1090:-1; // 977 : -1;
for (int ithread = 0; ithread < threads.length; ithread++) {
threads[ithread] = new Thread() {
public void run() {
......@@ -481,7 +485,7 @@ public class TexturedModel {
for (int i = 0; i < comb_clusters.length; i++) {
consolidated_clusters[comb_clusters[i]].add(cluster_list.get(i));
}
// incorrectly combined - first combo has only one cluster and NaN in the areas where other clusters could fit
if (debugLevel > 0) {
double [][] dbg_img = new double[this_combo][tiles];
double [][] dbg_borders = new double[this_combo][tiles];
......@@ -538,6 +542,7 @@ public class TexturedModel {
final int debugLevel)
{
final boolean batch_mode = clt_parameters.batch_run;
final boolean gltf_emissive = clt_parameters.gltf_emissive;
final int ref_index = scenes.length - 1;
final QuadCLT ref_scene = scenes[ref_index];
final TileProcessor tp = ref_scene.getTileProcessor();
......@@ -546,7 +551,8 @@ public class TexturedModel {
}
double infinity_disparity = ref_scene.getGeometryCorrection().getDisparityFromZ(clt_parameters.infinityDistance);
X3dOutput x3dOutput = null;
WavefrontExport wfOutput = null;
WavefrontExport wfOutput = null;
ArrayList<TriMesh> tri_meshes = null;
long startStepTime=System.nanoTime();
final int tilesX = tp.getTilesX();
final int tilesY = tp.getTilesY();
......@@ -609,15 +615,15 @@ public class TexturedModel {
TileCluster [] tileClusters = clusterizeFgBg( // wrong result type, not decided
tilesX, // final int tilesX,
ds_fg_bg, // final double [][] disparities, // may have more layers
sky_invert, // final boolean [] selected, // to remove sky (pre-filter by caller, like for ML?)
null, // sky_invert, // final boolean [] selected, // to remove sky (pre-filter by caller, like for ML?)
tex_disp_adiffo, // final double disp_adiffo,
tex_disp_rdiffo, // final double disp_rdiffo,
tex_disp_adiffd, // final double disp_adiffd,
tex_disp_rdiffd, // final double disp_rdiffd,
tex_disp_fof, // final double disp_fof, // enable higher difference (scale) for fried of a friend
debugLevel); //1); // 2); // final int debugLevel)
double [][] inter_weights = new double [tilesY][tilesX]; // per-tile texture weights for inter-scene accumulation;
double [][][][] inter_textures= new double [tilesY][tilesX][][]; // [channel][256] - non-overlapping textures
// double [][] inter_weights = new double [tilesY][tilesX]; // per-tile texture weights for inter-scene accumulation;
// double [][][][] inter_textures= new double [tilesY][tilesX][][]; // [channel][256] - non-overlapping textures
boolean [] scenes_sel = new boolean[scenes.length];
// for (int i = scenes.length - 10; i < scenes.length; i++) { // start with just one (reference) scene
for (int i = 0; i < scenes.length; i++) { // start with just one (reference) scene
......@@ -638,15 +644,9 @@ public class TexturedModel {
renormalize, // final boolean renormalize, // false - use normalizations from previous scenes to keep consistent colors
debugLevel); // final int debug_level)
boolean save_full_textures = false; // true;
boolean save_full_textures = true; // false; // true;
EyesisCorrectionParameters.CorrectionParameters correctionsParameters = ref_scene.correctionsParameters;
String x3d_dir= correctionsParameters.selectX3dDirectory(
//TODO: Which one to use - name or this.image_name ?
correctionsParameters.getModelName(ref_scene.getImageName()), // quad timestamp. Will be ignored if correctionsParameters.use_x3d_subdirs is false
correctionsParameters.x3dModelVersion,
true, // smart,
true); //newAllowed, // save
String x3d_dir = ref_scene.getX3dDirectory();
if (save_full_textures) {
for (int nslice = 0; nslice < combined_textures.length; nslice++) {
EyesisCorrections.saveAndShow(
......@@ -658,76 +658,42 @@ public class TexturedModel {
1); //
}
}
boolean save_individual_textures = true;
if (save_individual_textures) {
ImagePlus [] imp_textures = splitCombinedTextures(
tileClusters, // TileCluster [] tileClusters, //should have name <timestamp>-*
transform_size, // int transform_size,
combined_textures); // ImagePlus [] combo_textures )
for (int i = 0; i < imp_textures.length; i++) if (imp_textures[i] != null) { // should not be
EyesisCorrections.saveAndShow(
imp_textures[i], // imp_texture_cluster,
x3d_dir,
correctionsParameters.png,
false, // (nslice < 4), // clt_parameters.show_textures,
-1, // jpegQuality){// <0 - keep current, 0 - force Tiff, >0 use for JPEG
1); //
}
}
if (debugLevel > -100) {
return true;
// Maybe will switch to combined textures (less files)
ImagePlus [] imp_textures = splitCombinedTextures(
tileClusters, // TileCluster [] tileClusters, //should have name <timestamp>-*
transform_size, // int transform_size,
combined_textures); // ImagePlus [] combo_textures )
for (int i = 0; i < imp_textures.length; i++) if (imp_textures[i] != null) { // should not be
EyesisCorrections.saveAndShow(
imp_textures[i], // imp_texture_cluster,
x3d_dir,
correctionsParameters.png,
false, // (nslice < 4), // clt_parameters.show_textures,
-1, // jpegQuality){// <0 - keep current, 0 - force Tiff, >0 use for JPEG
1); //
}
ArrayList<CLTPass3d> clt_3d_passes = new ArrayList<CLTPass3d>(); // will use its own passes
int next_pass = 1; // temporarily
/*
if (clt_parameters.remove_scans){
System.out.println("Removing all scans but the first(background) and the last to save memory");
System.out.println("Will need to re-start the program to be able to output differently");
CLTPass3d latest_scan = tp.clt_3d_passes.get(tp.clt_3d_passes.size() - 1);
tp.trimCLTPasses(1);
tp.clt_3d_passes.add(latest_scan); // put it back
}
int next_pass = tp.clt_3d_passes.size(); //
// Create tasks to scan, have tasks, disparity and border tiles in tp.clt_3d_passes
tp.thirdPassSetupSurf( // prepare tile tasks for the second pass based on the previous one(s) // needs last scan
clt_parameters,
//FIXME: make a special parameter?
infinity_disparity, //0.25 * clt_parameters.bgnd_range, // double disparity_far,
clt_parameters.grow_disp_max, // other_range, //double disparity_near, //
ref_scene.getGeometryCorrection(),
threadsMax, // maximal number of threads to launch
updateStatus,
0); // final int debugLevel)
*/
// if (debugLevel > -100) {
// return true;
// }
if (!batch_mode && (debugLevel > -1)) {
tp.showScan(
tp.clt_3d_passes.get(next_pass-1), // CLTPass3d scan,
"after_pass3-"+(next_pass-1)); //String title)
}
String x3d_path = ref_scene.getX3dDirectory();
// create x3d file
if (clt_parameters.output_x3d) {
x3dOutput = new X3dOutput(
clt_parameters,
ref_scene.correctionsParameters,
ref_scene.getGeometryCorrection(),
tp.clt_3d_passes);
null);// tp.clt_3d_passes);
}
if (clt_parameters.output_obj && (x3d_path != null)) {
if (clt_parameters.output_obj && (x3d_dir != null)) {
try {
wfOutput = new WavefrontExport(
x3d_path,
x3d_dir,
ref_scene.correctionsParameters.getModelName(ref_scene.getImageName()),
clt_parameters,
ref_scene.correctionsParameters,
ref_scene.getGeometryCorrection(),
tp.clt_3d_passes);
null); // tp.clt_3d_passes);
} catch (IOException e) {
System.out.println("Failed to open Wavefront files for writing");
// TODO Auto-generated catch block
......@@ -735,187 +701,103 @@ public class TexturedModel {
// do nothing, just keep
}
}
if (x3dOutput != null) {
if (clt_parameters.output_glTF && (x3d_dir != null)) {
tri_meshes = new ArrayList<TriMesh>();
}
if (x3dOutput != null) { // 09.18.2022 For now - skipping background
x3dOutput.generateBackground(clt_parameters.infinityDistance <= 0.0); // needs just first (background) scan
}
int bgndIndex = 0; // it already exists?
CLTPass3d bgndScan = tp.clt_3d_passes.get(bgndIndex);
//TODO make it w/o need for bgndScan.texture as GPU will calculate texture right before output
//use selection? or texture_selection instead?
if (bgndScan.texture != null) { // TODO: same for the backdrop too
if (clt_parameters.infinityDistance > 0.0){ // generate background as a billboard
// grow selection, then grow once more and create border_tiles
// create/rstore, probably not needed
boolean [] bg_sel_backup = bgndScan.getSelected().clone();
boolean [] bg_border_backup = (bgndScan.getBorderTiles() == null) ? null: bgndScan.getBorderTiles().clone();
boolean [] bg_selected = bgndScan.getSelected();
boolean [] border_tiles = bg_selected.clone();
tp.growTiles(
2, // grow tile selection by 1 over non-background tiles 1: 4 directions, 2 - 8 directions, 3 - 8 by 1, 4 by 1 more
bg_selected,
null); // prohibit
for (int i = 0; i < border_tiles.length; i++){
border_tiles[i] = !border_tiles[i] && bg_selected[i];
}
// update texture_tiles (remove what is known not to be background
if (bgndScan.texture_tiles != null) { // for CPU
for (int ty = 0; ty < tilesY; ty++){
for (int tx = 0; tx < tilesX; tx++){
if (!bg_selected[tx + tilesX*ty]){
bgndScan.texture_tiles[ty][tx] = null; //
}
}
}
} else {// for GPU
for (int i = 0; i < bg_selected.length; i++) {
if (!bg_selected[i]) {
bgndScan.setTextureSelection(i,false);
}
}
}
//TODO2020: set texture_selection
bgndScan.setBorderTiles(border_tiles);
// limit tile_op to selection?
// updates selection from non-null texture tiles
String texturePath = ref_scene.getPassImage( // get image from a single pass - both CPU and GPU
clt_parameters,
colorProcParameters,
rgbParameters,
ref_scene.correctionsParameters.getModelName(ref_scene.getImageName())+"-img_infinity", // +scanIndex,
bgndIndex,
THREADS_MAX, // maximal number of threads to launch
updateStatus,
batch_mode ? -5: debugLevel);
if (texturePath != null) { // null if empty image
double [] scan_disparity = new double [tilesX * tilesY];
int indx = 0;
// boolean [] scan_selected = scan.getSelected();
for (int ty = 0; ty < tilesY; ty ++) for (int tx = 0; tx < tilesX; tx ++){
scan_disparity[indx++] = infinity_disparity;
}
// tp.showScan(
// scan, // CLTPass3d scan,
// "infinityDistance");
boolean showTri = false; // ((scanIndex < next_pass + 1) && clt_parameters.show_triangles) ||((scanIndex - next_pass) == 73);
try {
ref_scene.generateClusterX3d(
x3dOutput,
wfOutput, // output WSavefront if not null
texturePath,
"INFINITY", // id (scanIndex - next_pass), // id
"INFINITY", // class
bgndScan.getTextureBounds(),
bgndScan.getSelected(), // selected,
scan_disparity, // scan.disparity_map[ImageDtt.DISPARITY_INDEX_CM],
clt_parameters.transform_size,
clt_parameters.correct_distortions, // requires backdrop image to be corrected also
showTri, // (scanIndex < next_pass + 1) && clt_parameters.show_triangles,
infinity_disparity, // 0.3
clt_parameters.grow_disp_max, // other_range, // 2.0 'other_range - difference from the specified (*_CM)
clt_parameters.maxDispTriangle);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
// maybe not needed
bgndScan.setBorderTiles(bg_border_backup);
bgndScan.setSelected(bg_sel_backup);
}
}
}
// With GPU - do nothing here or copy selected -> texture_selection?
for (int scanIndex = next_pass; scanIndex < tp.clt_3d_passes.size(); scanIndex++){
if (debugLevel > 0){
System.out.println("FPGA processing scan #"+scanIndex);
// 09.18.2022 - skipping background generation
int num_clusters = -1;
for (int nscene=0; nscene < tileClusters.length; nscene++) {
for (int indx: tileClusters[nscene].getSubIndices()) {
if (indx > num_clusters) num_clusters= indx;
}
ref_scene.CLTMeasureTextures( // has GPU version - will just copy selection
clt_parameters,
scanIndex,
THREADS_MAX, // maximal number of threads to launch
updateStatus,
batch_mode ? -5: debugLevel);
}
for (int scanIndex = next_pass; (scanIndex < tp.clt_3d_passes.size()) && (scanIndex < clt_parameters.max_clusters); scanIndex++){ // just temporary limiting
num_clusters++;
for (int nslice = 0; nslice < tileClusters.length; nslice++){
if (debugLevel > -1){
System.out.println("Generating cluster images (limit is set to "+clt_parameters.max_clusters+") largest, scan #"+scanIndex);
}
String texturePath = ref_scene.getPassImage( // get image from a single pass
clt_parameters,
colorProcParameters,
rgbParameters,
ref_scene.correctionsParameters.getModelName(ref_scene.getImageName())+"-img"+scanIndex,
scanIndex,
THREADS_MAX, // maximal number of threads to launch
updateStatus,
batch_mode ? -5: debugLevel);
if (texturePath == null) { // not used in lwir
continue; // empty image
// System.out.println("Generating cluster images (limit is set to "+clt_parameters.max_clusters+") largest, scan #"+scanIndex);
System.out.println("Generating cluster images from texture slice "+nslice);
}
CLTPass3d scan = tp.clt_3d_passes.get(scanIndex);
// TODO: use new updated disparity, for now just what was forced for the picture
double [] scan_disparity = new double [tilesX * tilesY];
int indx = 0;
for (int ty = 0; ty < tilesY; ty ++) for (int tx = 0; tx < tilesX; tx ++){
scan_disparity[indx++] = scan.disparity[ty][tx];
}
if (clt_parameters.avg_cluster_disp){
double sw = 0.0, sdw = 0.0;
for (int i = 0; i< scan_disparity.length; i++){
// if (scan.selected[i] && !scan.border_tiles[i]){
if (scan.getSelected()[i] && !scan.getBorderTiles()[i]){
double w = scan.disparity_map[ImageDtt.DISPARITY_STRENGTH_INDEX][i];
sw +=w;
sdw += scan_disparity[i]*w;
int [] indices = tileClusters[nslice].getSubIndices();
Rectangle [] bounds = tileClusters[nslice].getSubBounds();
int dbg_tri_indx = 3; // showing triangles for cluster 3
for (int sub_i = 0; sub_i < indices.length; sub_i++) {
Rectangle roi = bounds[sub_i];
int cluster_index = indices[sub_i];
ImagePlus imp_texture_cluster = imp_textures[cluster_index];
if (imp_textures[cluster_index] == null) {
if (debugLevel > -1){
System.out.println("Empty cluster #"+cluster_index);
}
continue;
}
sdw/=sw;
for (int i = 0; i< scan_disparity.length; i++){
scan_disparity[i] = sdw;
String texturePath = imp_texture_cluster.getTitle()+".png";
double [] scan_disparity = tileClusters[nslice].getSubDisparity(sub_i);
boolean [] scan_selected = tileClusters[nslice].getSubSelected(sub_i);
// skipping averaging disparity fro a whole cluster (needs strength and does not seem to be useful)
boolean showTri = !batch_mode && (debugLevel > -1) && (clt_parameters.show_triangles) && (cluster_index == dbg_tri_indx);
try {
ref_scene.generateClusterX3d( // also generates wavefront obj
x3dOutput,
wfOutput, // output WSavefront if not null
tri_meshes, // ArrayList<TriMesh> tri_meshes,
texturePath,
"shape_id-"+cluster_index, // id
null, // class
roi, // scan.getTextureBounds(),
scan_selected, // scan.getSelected(),
scan_disparity, // scan.disparity_map[ImageDtt.DISPARITY_INDEX_CM],
clt_parameters.transform_size,
clt_parameters.correct_distortions, // requires backdrop image to be corrected also
showTri, // (scanIndex < next_pass + 1) && clt_parameters.show_triangles,
// FIXME: make a separate parameter:
infinity_disparity, // 0.25 * clt_parameters.bgnd_range, // 0.3
clt_parameters.grow_disp_max, // other_range, // 2.0 'other_range - difference from the specified (*_CM)
clt_parameters.maxDispTriangle,
clt_parameters.maxZtoXY, // double maxZtoXY, // 10.0. <=0 - do not use
clt_parameters.maxZ,
clt_parameters.limitZ,
debugLevel + 1); // int debug_level) > 0
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
boolean showTri = !batch_mode && (debugLevel > -1) && (((scanIndex < next_pass + 1) && clt_parameters.show_triangles) ||((scanIndex - next_pass) == 73));
// if (imp_textures[nslice] != null)
} // for (int nslice = 0; nslice < tileClusters.length; nslice++){
try {
ref_scene.generateClusterX3d(
x3dOutput,
wfOutput, // output WSavefront if not null
texturePath,
"shape_id-"+(scanIndex - next_pass), // id
null, // class
scan.getTextureBounds(),
scan.getSelected(),
scan_disparity, // scan.disparity_map[ImageDtt.DISPARITY_INDEX_CM],
clt_parameters.transform_size,
clt_parameters.correct_distortions, // requires backdrop image to be corrected also
showTri, // (scanIndex < next_pass + 1) && clt_parameters.show_triangles,
// FIXME: make a separate parameter:
infinity_disparity, // 0.25 * clt_parameters.bgnd_range, // 0.3
clt_parameters.grow_disp_max, // other_range, // 2.0 'other_range - difference from the specified (*_CM)
clt_parameters.maxDispTriangle);
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
if ((x3d_path != null) && (x3dOutput != null)){
x3dOutput.generateX3D(x3d_path+Prefs.getFileSeparator() + ref_scene.correctionsParameters.getModelName(ref_scene.getImageName())+".x3d");
if ((x3d_dir != null) && (x3dOutput != null)){
x3dOutput.generateX3D(x3d_dir+Prefs.getFileSeparator() + ref_scene.correctionsParameters.getModelName(ref_scene.getImageName())+".x3d");
}
if (wfOutput != null){
wfOutput.close();
System.out.println("Wavefront object file saved to "+wfOutput.obj_path);
System.out.println("Wavefront material file saved to "+wfOutput.mtl_path);
}
if (tri_meshes != null) {
try {
GlTfExport.glTFExport(
x3d_dir, // String x3d_dir,
ref_scene.correctionsParameters.getModelName(ref_scene.getImageName()), // String model_name,
tri_meshes, // ArrayList<TriMesh> tri_meshes,
gltf_emissive, // boolean gltf_emissive,
1);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // int debugLevel
}
// Save KML and ratings files if they do not exist (so not to overwrite edited ones), make them world-writable
ref_scene.writeKml (null, debugLevel);
ref_scene.writeRatingFile (debugLevel);
......@@ -1209,7 +1091,7 @@ public class TexturedModel {
}
} else { // for UM need to calculate min and max (probably OK for non-UM too !)
double [] minmax = QuadCLTCPU.getMinMaxTextures(
faded_textures ); //double [][][] textures // [nslices][nchn][i]
faded_textures ); //double [][][] textures // [slices][nchn][i]
rel_low = minmax[0];
rel_high = minmax[1];
}
......@@ -1322,19 +1204,19 @@ public class TexturedModel {
int transform_size,
ImagePlus [] combo_textures ) {
int max_cluster = -1;
for (int nscene=0; nscene < tileClusters.length; nscene++) {
for (int indx: tileClusters[nscene].getSubIndices()) {
for (int nslice=0; nslice < tileClusters.length; nslice++) {
for (int indx: tileClusters[nslice].getSubIndices()) {
if (indx > max_cluster) max_cluster= indx;
}
}
ImagePlus [] tex_clusters = new ImagePlus[max_cluster + 1];
for (int nscene=0; nscene < tileClusters.length; nscene++) {
String basename = combo_textures[nscene].getTitle();
for (int nslice=0; nslice < tileClusters.length; nslice++) {
String basename = combo_textures[nslice].getTitle();
basename = basename.substring(0,basename.indexOf('-'));
ImageStack combo_stack = combo_textures[nscene].getStack();
ImageStack combo_stack = combo_textures[nslice].getStack();
int nSlices = combo_stack.getSize();
int [] indices = tileClusters[nscene].getSubIndices();
Rectangle [] bounds = tileClusters[nscene].getSubBounds();
int [] indices = tileClusters[nslice].getSubIndices();
Rectangle [] bounds = tileClusters[nslice].getSubBounds();
// try to deal with a single-slice stack?
for (int i = 0; i < indices.length; i++) {
Rectangle roi = bounds[i];
......
......@@ -76,6 +76,60 @@ class TileCluster{
public Rectangle getBounds() {return bounds;}
public boolean [] getBorder() {return border;}
public double [] getDisparity() {return disparity;}
public double [] getSubDisparity(int indx) { // disparity should be NaN for unused !
if (clust_list == null) {
return null;
}
Rectangle sub_bounds = clust_list.get(indx).bounds;
double [] sub_disparity = new double [sub_bounds.width * sub_bounds.height];
int src_x = sub_bounds.x - bounds.x;
for (int dst_y = 0; dst_y < sub_bounds.height; dst_y++) {
int src_y = dst_y + sub_bounds.y - bounds.y;
System.arraycopy(
disparity,
src_y * bounds.width + src_x,
sub_disparity,
dst_y * sub_bounds.width,
sub_bounds.width);
}
return sub_disparity;
}
public boolean [] getSubBorder(int indx) { // disparity should be NaN for unused !
if (clust_list == null) {
return null;
}
Rectangle sub_bounds = clust_list.get(indx).bounds;
boolean [] sub_border = new boolean [sub_bounds.width * sub_bounds.height];
int src_x = sub_bounds.x - bounds.x;
for (int dst_y = 0; dst_y < sub_bounds.height; dst_y++) {
int src_y = dst_y + sub_bounds.y - bounds.y;
System.arraycopy(
border,
src_y * bounds.width + src_x,
sub_border,
dst_y * sub_bounds.width,
sub_bounds.width);
}
return sub_border;
}
public boolean [] getSubSelected(int indx) { // disparity should be NaN for unused !
if (clust_list == null) {
return null;
}
double [] sub_disparity = getSubDisparity(indx);
boolean [] sub_selection = new boolean [sub_disparity.length];
for (int i = 0; i < sub_disparity.length; i++) {
sub_selection[i] = !Double.isNaN(sub_disparity[i]);
}
return sub_selection;
}
public boolean [] getSelected() {
if (disparity == null) {
return null;
......@@ -132,42 +186,6 @@ class TileCluster{
}
/*
public TileCluster combine (TileCluster tileCluster) {
TileCluster outer, inner;
if (bounds.contains(tileCluster.bounds)) {
outer = this;
inner = tileCluster;
} else if (tileCluster.bounds.contains(bounds)) {
outer = tileCluster;
inner = this;
} else {
Rectangle outer_bounds = bounds.union(tileCluster.bounds);
outer = new TileCluster(outer_bounds, null, null);
outer.combine(this); //
inner = tileCluster;
}
int dst_x = inner.bounds.x - outer.bounds.x;
for (int src_y = 0; src_y < bounds.height; src_y++) {
int dst_y = src_y + inner.bounds.y - outer.bounds.y;
System.arraycopy(
inner.border,
src_y * bounds.width,
outer.border,
dst_y * outer.bounds.width + dst_x,
bounds.width);
System.arraycopy(
inner.disparity,
src_y * bounds.width,
outer.disparity,
dst_y * outer.bounds.width + dst_x,
bounds.width);
}
return outer;
}
*/
public void add(TileCluster tileCluster) {
if (!bounds.contains(tileCluster.bounds)) {
throw new IllegalArgumentException ("TileCluster.add(): Added cluster should fit into this ");
......@@ -193,16 +211,6 @@ class TileCluster{
disparity,
dst_y * bounds.width + dst_x,
tileCluster.bounds.width);
/**
if ((cluster_index != null) && (tileCluster.cluster_index != null)) {
System.arraycopy(
tileCluster.cluster_index,
src_y * tileCluster.bounds.width,
cluster_index,
dst_y * bounds.width + dst_x,
tileCluster.bounds.width);
}
*/
}
return;
}
......
......@@ -8626,12 +8626,24 @@ ImageDtt.startAndJoin(threads);
{
int [][] indices = new int [bounds.height][bounds.width];
int indx = 0;
for (int y = 0; y < bounds.height; y++) {
for (int x = 0; x < bounds.width; x++){
if (selected[this.tilesX * (bounds.y + y) + (bounds.x + x)]){
indices[y][x] = indx++;
} else {
indices[y][x] = -1;
if (selected.length > (bounds.height * bounds.width)) { // old version - selected is full size
for (int y = 0; y < bounds.height; y++) {
for (int x = 0; x < bounds.width; x++){
if (selected[this.tilesX * (bounds.y + y) + (bounds.x + x)]){
indices[y][x] = indx++;
} else {
indices[y][x] = -1;
}
}
}
} else { // 09.18.2022
for (int y = 0; y < bounds.height; y++) {
for (int x = 0; x < bounds.width; x++){
if (selected[bounds.width * y + x]){
indices[y][x] = indx++;
} else {
indices[y][x] = -1;
}
}
}
}
......@@ -8680,17 +8692,32 @@ ImageDtt.startAndJoin(threads);
int maxIndex = getMaxIndex(indices);
double [] indexedDisparity = new double [maxIndex+1];
int indx = 0;
for (int y = 0; indx <= maxIndex; y++) {
for (int x = 0; (x < width) && (indx <= maxIndex); x++){
if (indices[y][x] >=0){
// center coordinates for 8*8 tile is [3.5,3.5]
double disp = (disparity == null)? min_disparity:( disparity[(bounds.y + y) * tilesX + (bounds.x + x)]);
if (disp < min_disparity) disp = min_disparity;
else if (disp > max_disparity) disp = max_disparity;
indexedDisparity[indx] =disp;
indx ++;
if (disparity.length > (bounds.height * bounds.width)) { // old version - selected is full size
for (int y = 0; indx <= maxIndex; y++) {
for (int x = 0; (x < width) && (indx <= maxIndex); x++){
if (indices[y][x] >=0){
// center coordinates for 8*8 tile is [3.5,3.5]
double disp = (disparity == null)? min_disparity:( disparity[(bounds.y + y) * tilesX + (bounds.x + x)]);
if (disp < min_disparity) disp = min_disparity;
else if (disp > max_disparity) disp = max_disparity;
indexedDisparity[indx] =disp;
indx ++;
}
}
}
}
} else { // 09.18.2022
for (int y = 0; indx <= maxIndex; y++) {
for (int x = 0; (x < width) && (indx <= maxIndex); x++){
if (indices[y][x] >=0){
// center coordinates for 8*8 tile is [3.5,3.5]
double disp = (disparity == null)? min_disparity:( disparity[bounds.width *y + x]);
if (disp < min_disparity) disp = min_disparity;
else if (disp > max_disparity) disp = max_disparity;
indexedDisparity[indx] =disp;
indx ++;
}
}
}
}
return indexedDisparity;
}
......@@ -8711,23 +8738,45 @@ ImageDtt.startAndJoin(threads);
int maxIndex = getMaxIndex(indices);
double [][] coordinate = new double [maxIndex+1][];
int indx = 0;
for (int y = 0; indx <= maxIndex; y++) {
for (int x = 0; (x < width) && (indx <= maxIndex); x++){
if (indices[y][x] >=0){
// center coordinates for 8*8 tile is [3.5,3.5]
double px = (bounds.x + x + 0.5) * tile_size - 0.5;
double py = (bounds.y + y + 0.5) * tile_size - 0.5;
double disp = (disparity == null)? min_disparity:( disparity[(bounds.y + y) * tilesX + (bounds.x + x)]);
if (disp < min_disparity) disp = min_disparity;
else if (disp > max_disparity) disp = max_disparity;
coordinate[indx] = geometryCorrection.getWorldCoordinates(
px,
py,
disp,
correctDistortions);
indx ++;
if (disparity.length > (bounds.height * bounds.width)) { // old version - selected is full size
for (int y = 0; indx <= maxIndex; y++) {
for (int x = 0; (x < width) && (indx <= maxIndex); x++){
if (indices[y][x] >=0){
// center coordinates for 8*8 tile is [3.5,3.5]
double px = (bounds.x + x + 0.5) * tile_size - 0.5;
double py = (bounds.y + y + 0.5) * tile_size - 0.5;
double disp = (disparity == null)? min_disparity:( disparity[(bounds.y + y) * tilesX + (bounds.x + x)]);
if (disp < min_disparity) disp = min_disparity;
else if (disp > max_disparity) disp = max_disparity;
coordinate[indx] = geometryCorrection.getWorldCoordinates(
px,
py,
disp,
correctDistortions);
indx ++;
}
}
}
} else { // 09.18.2022
for (int y = 0; indx <= maxIndex; y++) {
for (int x = 0; (x < width) && (indx <= maxIndex); x++){
if (indices[y][x] >=0){
// center coordinates for 8*8 tile is [3.5,3.5]
double px = (bounds.x + x + 0.5) * tile_size - 0.5;
double py = (bounds.y + y + 0.5) * tile_size - 0.5;
double disp = (disparity == null)? min_disparity:( disparity[bounds.width * y + x]);
if (disp < min_disparity) disp = min_disparity;
else if (disp > max_disparity) disp = max_disparity;
coordinate[indx] = geometryCorrection.getWorldCoordinates(
px,
py,
disp,
correctDistortions);
indx ++;
}
}
}
}
return coordinate;
}
......@@ -8749,9 +8798,10 @@ ImageDtt.startAndJoin(threads);
public static int [][] filterTriangles(
int [][] triangles,
double [] disparity, // disparities per vertex index
double maxDispDiff) // maximal disparity difference in a triangle
{
final double min_avg = 0.5; // minimal average disparity to normalize triangle
double maxDispDiff, // maximal relative disparity difference in a triangle
int debug_level)
{
final double min_avg = 3.0; // 0.5; // minimal average disparity to normalize triangle
class Triangle {
int [] points = new int [3];
Triangle (int i1, int i2, int i3){
......@@ -8762,26 +8812,126 @@ ImageDtt.startAndJoin(threads);
}
ArrayList<Triangle> triList = new ArrayList<Triangle>();
for (int i = 0; i < triangles.length; i++){
double disp_avg = (triangles[i][0] + triangles[i][1]+ triangles[i][2])/3.0;
double disp_avg = (disparity[triangles[i][0]] + disparity[triangles[i][1]]+ disparity[triangles[i][2]])/3.0; // fixed 09.18.2022!
if (disp_avg < min_avg) disp_avg = min_avg;
loop:{
for (int j = 0; j < 3; j++){
int j1 = (j + 1) % 3;
if (Math.abs(disparity[triangles[i][j]] - disparity[triangles[i][j1]]) > (disp_avg* maxDispDiff)) break loop;
for (int j = 0; j < 3; j++){
int j1 = (j + 1) % 3;
if (Math.abs(disparity[triangles[i][j]] - disparity[triangles[i][j1]]) > (disp_avg* maxDispDiff)) {
if (debug_level > 1) {
System.out.println("removed triangle "+i+": "+
disparity[triangles[i][0]]+". "+disparity[triangles[i][1]]+". "+disparity[triangles[i][2]]+
". Avg = "+disp_avg);
}
break loop;
}
}
triList.add(new Triangle(
triangles[i][0],
triangles[i][1],
triangles[i][2]));
}
}
int [][] filteredTriangles = new int [triList.size()][3];
for (int i = 0; i < filteredTriangles.length; i++){
filteredTriangles[i] = triList.get(i).points;
}
return filteredTriangles;
}
public static int [][] filterTrianglesWorld(
int [][] triangles,
double [][] worldXYZ, // world per vertex index
double maxZtoXY,
double maxZ)
{
final double maxZtoXY2 = maxZtoXY * maxZtoXY;
class Triangle {
int [] points = new int [3];
Triangle (int i1, int i2, int i3){
points[0] = i1;
points[1] = i2;
points[2] = i3;
}
triList.add(new Triangle(
triangles[i][0],
triangles[i][1],
triangles[i][2]));
}
ArrayList<Triangle> triList = new ArrayList<Triangle>();
for (int i = 0; i < triangles.length; i++){
double [][] min_max = new double[3][2];
boolean not_too_far = true;
for (int di = 0; di < 3; di++) {
min_max[di][0] = worldXYZ[triangles[i][0]][di];
min_max[di][1] = min_max[di][0]; // both min and max to the same vertex 0
}
if (maxZ != 0) {
not_too_far &= worldXYZ[triangles[i][0]][2] > -maxZ;
}
for (int vi = 1; vi < 3; vi++) {
for (int di = 0; di < 3; di++) {
min_max[di][0] = Math.min(min_max[di][0], worldXYZ[triangles[i][vi]][di]);
min_max[di][1] = Math.max(min_max[di][1], worldXYZ[triangles[i][vi]][di]);
}
}
double dx = min_max[0][1]-min_max[0][0];
double dy = min_max[1][1]-min_max[1][0];
double dz = min_max[2][1]-min_max[2][0];
double ratio2 = dz*dz/(dx*dx+dy*dy + 0.001);
if (not_too_far && ((maxZtoXY == 0) || (ratio2 < maxZtoXY2))) {
triList.add(new Triangle(
triangles[i][0],
triangles[i][1],
triangles[i][2]));
}
}
int [][] filteredTriangles = new int [triList.size()][3];
for (int i = 0; i < filteredTriangles.length; i++){
filteredTriangles[i] = triList.get(i).points;
}
return filteredTriangles;
}
}
public static int [] reIndex(
int [][] indices,
int [][] triangles) {
int last_index = -1;
for (int i = 0; i < indices.length; i++) {
for (int j = 0; j < indices[i].length; j++) {
if (indices[i][j] > last_index) {
last_index = indices[i][j];
}
}
}
boolean [] used_indices = new boolean[last_index+1];
for (int i = 0; i < triangles.length; i++) {
for (int j = 0; j < triangles[i].length; j++) { // always 3
used_indices[triangles[i][j]] = true;
}
}
int new_len = 0;
for (int i = 0; i < used_indices.length; i++) if (used_indices[i]) {
new_len++;
}
if (new_len == used_indices.length) {
return null; // no re-indexing is needed
}
int [] re_index = new int [new_len];
int indx = 0;
for (int i = 0; i < indices.length; i++) {
for (int j = 0; j < indices[i].length; j++) {
int old_index=indices[i][j];
if (old_index >= 0) {
if (used_indices[old_index]) { // keep
re_index[indx] = old_index;
indices[i][j] = indx++;
} else {
indices[i][j] = -1;
}
}
}
}
return re_index;
}
public static int [][] triangulateCluster(
int [][] indices)
......@@ -8851,13 +9001,27 @@ ImageDtt.startAndJoin(threads);
{
String [] titles = {"disparity","triangles"};
double [][] dbg_img = new double [titles.length][tilesX*tilesY*tile_size*tile_size];
for (int i = 0; i < selected.length; i++ ){
double d = selected[i]? ((disparity.length >1) ? disparity[i] : disparity[0]):Double.NaN;
int y = i / tilesX;
int x = i % tilesX;
for (int dy = 0; dy <tile_size; dy ++){
for (int dx = 0; dx <tile_size; dx ++){
dbg_img[0][(y * tile_size + dy)*(tile_size*tilesX) + (x * tile_size + dx)] = d;
Arrays.fill(dbg_img[0], Double.NaN);
if (selected.length > (bounds.height * bounds.width)) { // old version - selected is full size
for (int i = 0; i < selected.length; i++ ){
double d = selected[i]? ((disparity.length >1) ? disparity[i] : disparity[0]):Double.NaN;
int y = i / tilesX;
int x = i % tilesX;
for (int dy = 0; dy <tile_size; dy ++){
for (int dx = 0; dx <tile_size; dx ++){
dbg_img[0][(y * tile_size + dy)*(tile_size*tilesX) + (x * tile_size + dx)] = d;
}
}
}
} else { // 09.18.2022
for (int i = 0; i < selected.length; i++ ){
double d = selected[i]? ((disparity.length > 1) ? disparity[i] : disparity[0]):Double.NaN;
int y = i / bounds.width + bounds.y;
int x = i % bounds.width + bounds.x;
for (int dy = 0; dy <tile_size; dy ++){
for (int dx = 0; dx <tile_size; dx ++){
dbg_img[0][(y * tile_size + dy)*(tile_size*tilesX) + (x * tile_size + dx)] = d;
}
}
}
}
......@@ -8885,12 +9049,7 @@ ImageDtt.startAndJoin(threads);
for (int j = 0; j < tile_size; j++){
int x = pxy[pntIndx[0]][0] + dx*j;
int y = pxy[pntIndx[0]][1] + dy*j;
// int indx = y * tile_size * tilesX + x;
// if (indx < dbg_img[1].length) {
dbg_img[1][y * tile_size * tilesX + x] = 10.0; //1711748
// } else {
// indx += 0;
// }
}
}
}
......
package com.elphel.imagej.x3d.export;
/**
**
** GlTfExport - generate glTF representation of the model
**
** Copyright (C) 2022 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** GlTfExport.java 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 3 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, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import ij.Prefs;
public class GlTfExport {
public static int NEAREST = 9728;
public static int LINEAR = 9729;
public static int NEAREST_MIPMAP_NEAREST = 9984;
public static int LINEAR_MIPMAP_NEAREST = 9985;
public static int NEAREST_MIPMAP_LINEAR = 9986;
public static int LINEAR_MIPMAP_LINEAR = 9986;
public static int CLAMP_TO_EDGE = 33071;
public static int MIRRORED_REPEAT = 33648;
public static int REPEAT = 10497;
public static int ARRAY_BUFFER = 34962; // for attributes
public static int ELEMENT_ARRAY_BUFFER = 34963; // for indices
public static int COMPONENT_SIGNED_BYTE = 5120;
public static int COMPONENT_UNSIGNED_BYTE = 5121;
public static int COMPONENT_SIGNED_SHORT = 5122;
public static int COMPONENT_UNSIGNED_SHORT = 5123;
public static int COMPONENT_UNSIGNED_INT = 5125;
public static int COMPONENT_FLOAT = 5126;
public static String ALPHAMODE_OPAQUE = "OPAQUE";
public static String ALPHAMODE_MASK = "MASK";
public static String ALPHAMODE_BLEND = "BLEND";
public static String TYPE_SCALAR = "SCALAR"; // 1
public static String TYPE_VEC2 = "VEC2"; // 2
public static String TYPE_VEC3 = "VEC3"; // 3
public static String TYPE_VEC4 = "VEC4"; // 4
public static String TYPE_MAT2 = "MAT2"; // 4
public static String TYPE_MAT3 = "MAT3"; // 9
public static String TYPE_MAT4 = "MAT4"; // 16
public static int MODE_POINTS = 0;
public static int MODE_LINES = 1;
public static int MODE_LINE_LOOP = 2;
public static int MODE_LINE_STRIP = 3;
public static int MODE_TRIANGLES = 4;
public static int MODE_TRIANGLE_STRIP = 5;
public static int MODE_TRIANGLE_FAN = 6;
public static void glTFExport(
String x3d_dir,
String model_name,
ArrayList<TriMesh> tri_meshes,
boolean gltf_emissive,
int debugLevel
) throws IOException, JSONException {
boolean invert_faces = true; // false; // true; // false; // true;
boolean [] inv_xyz = {false,false,false};
boolean [] inv_tex = {false,true};
int [] triangle = {0,1,2};
if (invert_faces) {
triangle[0]=2;
triangle[2]=0;
}
String base_name = x3d_dir + Prefs.getFileSeparator() + model_name;
String bin_path = base_name+".bin";
String bin_name = model_name+".bin";
String gltf_path = base_name+".gltf";
int total_meshes = tri_meshes.size();
int total_vertices = 0;
// int total_triangles = 0;
int [] mesh_vert = new int [total_meshes];
int [] mesh_tri = new int [total_meshes];
int [][] offset_len_indx = new int [total_meshes][2];
int [][] offset_len_tex = new int [total_meshes][2];
int [][] offset_len_coor = new int [total_meshes][2];
int [][] minmax_indx = new int [total_meshes][2];
float [][][] minmax_tex = new float [total_meshes][2][2]; // [][{x,y}[{min, max}]
float [][][] minmax_coor = new float [total_meshes][3][2]; // [][{x,y,z}[{min, max}]
int index_size = 4;// 2; // 4 bytes per index entry - will use int - probably faster render even if more memory
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
mesh_vert[nmesh] = tri_meshes.get(nmesh).getCoordinates().length;
mesh_tri[nmesh] = tri_meshes.get(nmesh).getTriangles().length;
total_vertices += mesh_vert[nmesh];
/// total_triangles += mesh_tri[nmesh];
offset_len_indx[nmesh][1] = mesh_tri[nmesh] * index_size * 3;
offset_len_tex [nmesh][1] = mesh_vert[nmesh] * 4 * 2; // float, VEC2
offset_len_coor[nmesh][1] = mesh_vert[nmesh] * 4 * 3; // float, VEC3
if (nmesh > 0) {
int index_prev_len4 = 4 * ((offset_len_indx[nmesh - 1][1] + 3)/4);
offset_len_indx[nmesh][0] = offset_len_indx[nmesh - 1][0] + index_prev_len4; // pad to 4 bytes
offset_len_tex [nmesh][0] = offset_len_tex [nmesh - 1][0] + offset_len_tex [nmesh - 1][1];
offset_len_coor[nmesh][0] = offset_len_coor[nmesh - 1][0] + offset_len_coor[nmesh - 1][1];
}
}
int buffer_index_bytes = offset_len_indx[total_meshes-1][0]+offset_len_indx[total_meshes-1][1];
buffer_index_bytes = 4 * ((buffer_index_bytes + 3)/4);
int buffer_texture_bytes = 4 * total_vertices * 2; // VEC2
int buffer_coordinate_bytes = 4 * total_vertices * 3; // VEC3
int buffer_total_bytes = buffer_index_bytes+ buffer_texture_bytes+ buffer_coordinate_bytes;
if (debugLevel > -1) {
System.out.println("glTFExport(): allocating "+buffer_total_bytes+" bytes buffer");
System.out.println(buffer_index_bytes+" for indices,\n" +
buffer_texture_bytes+" for texture coordinates, and \n" +
buffer_coordinate_bytes+" for vertice coordinates.");
}
ByteBuffer bb0 = ByteBuffer.allocate(buffer_total_bytes);
ByteBuffer bb = bb0.order(ByteOrder.LITTLE_ENDIAN);
// will be 3 buffer views: 0 for indices, 1 - for texture coordinates and 2 - for coordinates
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
int [][] triangles = tri_meshes.get(nmesh).getTriangles();
if (index_size == 2) {
for (int i = 0; i < triangles.length; i++) {
bb.putShort((short) (triangles[i][triangle[0]]));
bb.putShort((short) (triangles[i][triangle[1]]));
bb.putShort((short) (triangles[i][triangle[2]]));
bb.putShort((short) 0);
}
} else {
for (int i = 0; i < triangles.length; i++) {
bb.putInt(triangles[i][triangle[0]]);
bb.putInt(triangles[i][triangle[1]]);
bb.putInt(triangles[i][triangle[2]]);
}
}
minmax_indx[nmesh][0] = triangles[0][0];
minmax_indx[nmesh][1] = minmax_indx[nmesh][0];
for (int i = 0; i < triangles.length; i++) {
for (int j = 0; j < triangles[i].length; j++) {
if (triangles[i][j] < minmax_indx[nmesh][0]) {
minmax_indx[nmesh][0] = triangles[i][j];
} else if (triangles[i][j] > minmax_indx[nmesh][1]) {
minmax_indx[nmesh][1] = triangles[i][j];
}
}
}
}
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
double [][] tex_coord = tri_meshes.get(nmesh).getTexCoord();
for (int i = 0; i < tex_coord.length; i++) {
for (int j = 0; j < tex_coord[i].length; j++) {
if (inv_tex[j]) tex_coord[i][j] = 1.0-tex_coord[i][j];
}
}
for (int j = 0; j < tex_coord[0].length; j++) {
minmax_tex[nmesh][j][0] = (float) tex_coord[0][j];
minmax_tex[nmesh][j][1] = minmax_tex[nmesh][j][0];
}
for (int i = 0; i < tex_coord.length; i++) {
bb.putFloat((float) tex_coord[i][0]);
bb.putFloat((float) tex_coord[i][1]);
for (int j = 0; j < tex_coord[i].length; j++) {
float f = (float) tex_coord[i][j];
if (f < minmax_tex[nmesh][j][0]) {
minmax_tex[nmesh][j][0] = f;
} else if (f > minmax_tex[nmesh][j][1]) {
minmax_tex[nmesh][j][1] = f;
}
}
}
}
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
double [][] coordinates = tri_meshes.get(nmesh).getCoordinates();
for (int i = 0; i < coordinates.length; i++) {
for (int j = 0; j < coordinates[i].length; j++) {
if (inv_xyz[j]) coordinates[i][j] = -coordinates[i][j];
}
}
for (int j = 0; j < coordinates[0].length; j++) {
minmax_coor[nmesh][j][0] = (float) coordinates[0][j];
minmax_coor[nmesh][j][1] = minmax_coor[nmesh][j][0];
}
for (int i = 0; i < coordinates.length; i++) {
bb.putFloat((float) coordinates[i][0]);
bb.putFloat((float) coordinates[i][1]);
bb.putFloat((float) coordinates[i][2]);
for (int j = 0; j < coordinates[i].length; j++) {
float f = (float) coordinates[i][j];
if (f < minmax_coor[nmesh][j][0]) {
minmax_coor[nmesh][j][0] = f;
} else if (f > minmax_coor[nmesh][j][1]) {
minmax_coor[nmesh][j][1] = f;
}
}
}
}
bb.flip();
try (// write bb to file
@SuppressWarnings("resource")
FileChannel fc = new FileOutputStream(bin_path).getChannel()) {
fc.write(bb);
fc.close();
}
if (debugLevel > -1) {
System.out.println("glTFExport(): wrote binary data to "+bin_path);
}
// Now generate glTF json
// prepare mesh short names such as "img001"
String [] short_names = new String[total_meshes];
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
String tex_uri = tri_meshes.get(nmesh).getImage();
short_names[nmesh] = tex_uri.substring(tex_uri.indexOf("-")+1,tex_uri.lastIndexOf("-"));
}
JSONObject gltf_json = new JSONObject();
//create asset
JSONObject gltf_asset = new JSONObject();
gltf_asset.put("generator", "imagej-elphel");
gltf_asset.put("copyright", "2022 (C) Elphel, Inc.");
gltf_asset.put("version", "2.0");
gltf_json.put("asset", gltf_asset);
// set single scene
gltf_json.put("scene", 0);
// Create nodes -> change to a single node, single mesh with multiple primitives
JSONArray gltf_nodes = new JSONArray();
JSONObject gltf_node0 = new JSONObject();
gltf_node0.put("name", "all-meshes-node");
gltf_node0.put("mesh", 0); // index of a single mesh
gltf_nodes.put(gltf_node0);
gltf_json.put("nodes", gltf_nodes);
// Create a single scene0
JSONArray gltf_scene0_nodes = new JSONArray();
gltf_scene0_nodes.put(0);
JSONObject gltf_scene0 = new JSONObject();
gltf_scene0.put("name", model_name);
gltf_scene0.put("nodes", gltf_scene0_nodes);
// Crete scenes
JSONArray gltf_scenes = new JSONArray();
gltf_scenes.put(gltf_scene0);
gltf_json.put("scenes", gltf_scenes);
// Create images
JSONArray gltf_images = new JSONArray();
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
JSONObject gltf_image = new JSONObject();
gltf_image.put("uri", tri_meshes.get(nmesh).getImage());
gltf_images.put(gltf_image);
}
gltf_json.put("images", gltf_images);
// Create textures
JSONArray gltf_textures = new JSONArray();
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
JSONObject gltf_texture = new JSONObject();
gltf_texture.put("sampler", 0);
gltf_texture.put("source", nmesh);
gltf_texture.put("name", short_names[nmesh]+"-tex");
gltf_textures.put(gltf_texture);
}
gltf_json.put("textures", gltf_textures);
// Create sampler0
JSONObject gltf_sampler0 = new JSONObject();
// page 46 - otherwise needs power-of-two textures
/*
3.8.4.5. Non-power-of-two Textures
Client implementations SHOULD resize non-power-of-two textures (so that their horizontal and
vertical sizes are powers of two) when running on platforms that have limited support for such
texture dimensions.
Implementation Note
Specifically, if the sampler the texture references:
- has a wrapping mode (either wrapS or wrapT) equal to repeat or mirrored repeat,
or
- has a minification filter (minFilter) that uses mipmapping.
*/
gltf_sampler0.put("magFilter", LINEAR);
gltf_sampler0.put("minFilter", LINEAR); // NEAREST_MIPMAP_LINEAR);
gltf_sampler0.put("wrapS", CLAMP_TO_EDGE); // REPEAT); // CLAMP_TO_EDGE
gltf_sampler0.put("wrapT", CLAMP_TO_EDGE); //REPEAT); // CLAMP_TO_EDGE
// Create samplers
JSONArray gltf_samplers = new JSONArray();
gltf_samplers.put(gltf_sampler0);
gltf_json.put("samplers", gltf_samplers);
// Create materials (material references texture, so each mesh object - new material
JSONArray gltf_materials = new JSONArray();
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
JSONObject gltf_material = new JSONObject();
JSONObject gltf_pbrMetallicRoughness = new JSONObject();
JSONObject gltf_baseColorTexture = new JSONObject();
gltf_baseColorTexture.put("index", nmesh); // reference corresponding texture
gltf_pbrMetallicRoughness.put("baseColorTexture", gltf_baseColorTexture);
gltf_pbrMetallicRoughness.put("metallicFactor", 0.0);
gltf_material.put("pbrMetallicRoughness",gltf_pbrMetallicRoughness);
gltf_material.put("name", short_names[nmesh]+"-mat");
gltf_material.put("alphaMode", ALPHAMODE_BLEND);
if (gltf_emissive) {
JSONObject gltf_emissiveTexture = new JSONObject();
gltf_emissiveTexture.put("index", nmesh);
gltf_material.put("emissiveTexture", gltf_emissiveTexture);
JSONArray gltf_material_emissiveFactor = new JSONArray();
gltf_material_emissiveFactor.put(1.0);
gltf_material_emissiveFactor.put(1.0);
gltf_material_emissiveFactor.put(1.0);
gltf_material.put("emissiveFactor", gltf_material_emissiveFactor);
}
gltf_materials.put(gltf_material);
}
gltf_json.put("materials", gltf_materials);
// create buffer(s) - a single one
// Create buffer0 (only one)
JSONObject gltf_buffer0 = new JSONObject();
gltf_buffer0.put("byteLength", buffer_total_bytes);
gltf_buffer0.put("uri", bin_name); // relative
gltf_buffer0.put("name", "single-buffer");
// Create buffers
JSONArray gltf_buffers = new JSONArray();
gltf_buffers.put(gltf_buffer0);
gltf_json.put("buffers", gltf_buffers);
// create bufferViews - 3 per mesh: index, tex_coords, coordinates
JSONArray gltf_bufferViews = new JSONArray();
// bufferView for triangle indices:
JSONObject gltf_view_index = new JSONObject();
gltf_view_index.put("buffer", 0);
gltf_view_index.put("byteOffset", 0);
gltf_view_index.put("byteLength", buffer_index_bytes);
/// gltf_view_index.put("byteStride", 2*index_size); // Probably not needed ? page 86
gltf_view_index.put("target", ELEMENT_ARRAY_BUFFER);
gltf_view_index.put("name", "buffer-view-index");
gltf_bufferViews.put(gltf_view_index);
// bufferView for texture coordinates
JSONObject gltf_view_tex = new JSONObject();
gltf_view_tex.put("buffer", 0);
gltf_view_tex.put("byteOffset", buffer_index_bytes);
gltf_view_tex.put("byteLength", buffer_texture_bytes);
gltf_view_tex.put("byteStride", 2*4);
gltf_view_tex.put("target", ARRAY_BUFFER);
gltf_view_tex.put("name", "buffer-view-tex-coor");
gltf_bufferViews.put(gltf_view_tex);
// bufferView for world coordinates
JSONObject gltf_view_coor = new JSONObject();
gltf_view_coor.put("buffer", 0);
gltf_view_coor.put("byteOffset", buffer_index_bytes+ buffer_texture_bytes);
gltf_view_coor.put("byteLength", buffer_coordinate_bytes);
gltf_view_coor.put("byteStride", 3 * 4);
gltf_view_coor.put("target", ARRAY_BUFFER);
gltf_view_coor.put("name", "buffer-view-world-coor");
gltf_bufferViews.put(gltf_view_coor);
gltf_json.put("bufferViews", gltf_bufferViews);
// generate accessors - 3 per mesh: index, texture coordinates and world coordinates
JSONArray gltf_accessors = new JSONArray();
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
// accessor for triangle indices
JSONObject gltf_accessor_index = new JSONObject();
JSONArray gltf_accessor_index_min = new JSONArray();
JSONArray gltf_accessor_index_max = new JSONArray();
gltf_accessor_index_min.put(minmax_indx[nmesh][0]);
gltf_accessor_index_max.put(minmax_indx[nmesh][1]);
gltf_accessor_index.put("bufferView", 0); // of 3
gltf_accessor_index.put("byteOffset", offset_len_indx[nmesh][0]);
// actually it is signed short/signed int
gltf_accessor_index.put("componentType", (index_size == 2)? COMPONENT_UNSIGNED_SHORT : COMPONENT_UNSIGNED_INT);
gltf_accessor_index.put("count", 3 * mesh_tri[nmesh]);
gltf_accessor_index.put("min", gltf_accessor_index_min);
gltf_accessor_index.put("max", gltf_accessor_index_max);
gltf_accessor_index.put("type", TYPE_SCALAR);
gltf_accessor_index.put("name", short_names[nmesh]+"-accessor-index-"+(3*nmesh+0));
gltf_accessors.put(gltf_accessor_index);
// accessor for texture coordinates
JSONObject gltf_accessor_texcoor = new JSONObject();
JSONArray gltf_accessor_texcoor_min = new JSONArray();
JSONArray gltf_accessor_texcoor_max = new JSONArray();
gltf_accessor_texcoor_min.put(minmax_tex[nmesh][0][0]);
gltf_accessor_texcoor_min.put(minmax_tex[nmesh][1][0]);
gltf_accessor_texcoor_max.put(minmax_tex[nmesh][0][1]);
gltf_accessor_texcoor_max.put(minmax_tex[nmesh][1][1]);
gltf_accessor_texcoor.put("bufferView", 1); // of 3
gltf_accessor_texcoor.put("byteOffset", offset_len_tex[nmesh][0]);
// actually it is signed short/signed int
gltf_accessor_texcoor.put("componentType", COMPONENT_FLOAT);
gltf_accessor_texcoor.put("count", mesh_vert[nmesh]);
gltf_accessor_texcoor.put("min", gltf_accessor_texcoor_min);
gltf_accessor_texcoor.put("max", gltf_accessor_texcoor_max);
gltf_accessor_texcoor.put("type", TYPE_VEC2);
gltf_accessor_texcoor.put("name", short_names[nmesh]+"-accessor-texcoor-"+(3*nmesh+1));
gltf_accessors.put(gltf_accessor_texcoor);
// accessor for world coordinates
JSONObject gltf_accessor_coor = new JSONObject();
JSONArray gltf_accessor_coor_min = new JSONArray();
JSONArray gltf_accessor_coor_max = new JSONArray();
gltf_accessor_coor_min.put(minmax_coor[nmesh][0][0]);
gltf_accessor_coor_min.put(minmax_coor[nmesh][1][0]);
gltf_accessor_coor_min.put(minmax_coor[nmesh][2][0]);
gltf_accessor_coor_max.put(minmax_coor[nmesh][0][1]);
gltf_accessor_coor_max.put(minmax_coor[nmesh][1][1]);
gltf_accessor_coor_max.put(minmax_coor[nmesh][2][1]);
gltf_accessor_coor.put("bufferView", 2); // of 3
gltf_accessor_coor.put("byteOffset", offset_len_coor[nmesh][0]);
// actually it is signed short/signed int
gltf_accessor_coor.put("componentType", COMPONENT_FLOAT);
gltf_accessor_coor.put("count", mesh_vert[nmesh]);
gltf_accessor_coor.put("min", gltf_accessor_coor_min);
gltf_accessor_coor.put("max", gltf_accessor_coor_max);
gltf_accessor_coor.put("type", TYPE_VEC3);
gltf_accessor_coor.put("name", short_names[nmesh]+"-accessor-coor-"+(3*nmesh+2));
gltf_accessors.put(gltf_accessor_coor);
}
gltf_json.put("accessors", gltf_accessors);
// Generate meshes - trying a single mesh with multiple primitives
JSONObject gltf_mesh0 = new JSONObject();
JSONArray gltf_mesh0_primitives = new JSONArray();
for (int nmesh = 0; nmesh < total_meshes; nmesh++) {
JSONObject gltf_mesh0_primitive = new JSONObject(); // does not have "name"
JSONObject gltf_mesh0_primitive_attr = new JSONObject();
gltf_mesh0_primitive_attr.put("POSITION", 3 * nmesh + 2);
gltf_mesh0_primitive_attr.put("TEXCOORD_0", 3 * nmesh + 1);
gltf_mesh0_primitive.put("attributes", gltf_mesh0_primitive_attr);
gltf_mesh0_primitive.put("indices", 3 * nmesh + 0);
gltf_mesh0_primitive.put("mode", MODE_TRIANGLES);
gltf_mesh0_primitive.put("material", nmesh);
gltf_mesh0_primitives.put(gltf_mesh0_primitive);
}
gltf_mesh0.put("primitives", gltf_mesh0_primitives);
gltf_mesh0.put("name", "all-model-meshes");
JSONArray gltf_meshes = new JSONArray();
gltf_meshes.put(gltf_mesh0); // just a single mesh with multiple primitives
gltf_json.put("meshes", gltf_meshes);
Gson gson = new GsonBuilder().setPrettyPrinting().create();
JsonElement je = JsonParser.parseString(gltf_json.toString());
String prettyJsonString = gson.toJson(je);
FileWriter gltf_writer; // f0 = new FileWriter("output.txt");
gltf_writer = new FileWriter(gltf_path);
gltf_writer.write(prettyJsonString);
gltf_writer.close();
return;
}
}
package com.elphel.imagej.x3d.export;
/**
**
** TriMesh - triangular mesh representation
**
** Copyright (C) 2022 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** TriMesh.java 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 3 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, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
public class TriMesh {
public String texture_image;
public double [][] worldXYZ;
public double [][] texCoord;
public int [][] triangles;
public TriMesh (
String texture_image,
double [][] worldXYZ,
double [][] texCoord,
int [][] triangles) {
this.texture_image = texture_image;
this.worldXYZ = worldXYZ;
this.texCoord = texCoord;
this.triangles = triangles;
}
public String getImage() {return texture_image;}
int [][] getTriangles() {return triangles;}
double [][] getTexCoord() {
return texCoord;
}
public double [][] getTexCoord(boolean inv_x, boolean inv_y, boolean swap_xy) {
if (!inv_x && !inv_y && !swap_xy) {
return texCoord;
}
double [][] inv_tex_coord = new double [texCoord.length][2];
double scale_x = inv_x ? -1.0 : 1.0;
double scale_y = inv_y ? -1.0 : 1.0;
if (swap_xy) {
for (int i = 0; i <texCoord.length; i++) {
inv_tex_coord[i][0] = scale_y * texCoord[i][1];
inv_tex_coord[i][1] = scale_x * texCoord[i][0];
}
} else {
for (int i = 0; i <texCoord.length; i++) {
inv_tex_coord[i][0] = scale_x * texCoord[i][0];
inv_tex_coord[i][1] = scale_y * texCoord[i][1];
}
}
return inv_tex_coord;
}
public double [][] getCoordinates(){
return worldXYZ;
}
/**
* 0: XYZ -> XYZ
* 1: XYZ -> YZX
* 2: XYZ -> ZXY
* 3: XYZ -> XZY
* 4: XYZ -> ZYX
* 5: XYZ -> YXZ
* @param inv_x
* @param inv_y
* @param inv_z
* @param swap3
* @return
*/
public double [][] getCoordinates(boolean inv_x, boolean inv_y, boolean inv_z, int swap3) {
if (!inv_x && !inv_y && !inv_y && (swap3 == 0)) {
return worldXYZ;
}
double [][] inv_worldXYZ = new double [worldXYZ.length][3];
double scale_x = inv_x ? -1.0 : 1.0;
double scale_y = inv_y ? -1.0 : 1.0;
double scale_z = inv_z ? -1.0 : 1.0;
switch (swap3) {
case 0: // XYZ -> XYZ
for (int i = 0; i <texCoord.length; i++) {
inv_worldXYZ[i][0] = scale_x * worldXYZ[i][0];
inv_worldXYZ[i][1] = scale_y * worldXYZ[i][1];
inv_worldXYZ[i][2] = scale_z * worldXYZ[i][2];
}
break;
case 1: // XYZ -> YZX
for (int i = 0; i <texCoord.length; i++) {
inv_worldXYZ[i][1] = scale_x * worldXYZ[i][0];
inv_worldXYZ[i][2] = scale_y * worldXYZ[i][1];
inv_worldXYZ[i][0] = scale_z * worldXYZ[i][2];
}
break;
case 2: // XYZ -> ZXY
for (int i = 0; i <texCoord.length; i++) {
inv_worldXYZ[i][2] = scale_x * worldXYZ[i][0];
inv_worldXYZ[i][0] = scale_y * worldXYZ[i][1];
inv_worldXYZ[i][1] = scale_z * worldXYZ[i][2];
}
break;
case 3: // XYZ -> XZY
for (int i = 0; i <texCoord.length; i++) {
inv_worldXYZ[i][0] = scale_x * worldXYZ[i][0];
inv_worldXYZ[i][2] = scale_y * worldXYZ[i][1];
inv_worldXYZ[i][1] = scale_z * worldXYZ[i][2];
}
break;
case 4: // XYZ -> ZYX
for (int i = 0; i <texCoord.length; i++) {
inv_worldXYZ[i][2] = scale_x * worldXYZ[i][0];
inv_worldXYZ[i][1] = scale_y * worldXYZ[i][1];
inv_worldXYZ[i][0] = scale_z * worldXYZ[i][2];
}
break;
case 5: // XYZ -> YXZ
for (int i = 0; i <texCoord.length; i++) {
inv_worldXYZ[i][1] = scale_x * worldXYZ[i][0];
inv_worldXYZ[i][0] = scale_y * worldXYZ[i][1];
inv_worldXYZ[i][2] = scale_z * worldXYZ[i][2];
}
break;
default: return null;
}
return inv_worldXYZ;
}
}
package com.elphel.imagej.x3d.export;
/**
**
** WavefrontExport - generate Wavefront OBJ representation of the model
**
** Copyright (C) 2018 Elphel, Inc.
**
** -----------------------------------------------------------------------------**
**
** WavefrontExport.java 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 3 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, see <http://www.gnu.org/licenses/>.
** -----------------------------------------------------------------------------**
**
*/
import java.io.FileWriter;
import java.io.IOException;
......
......@@ -76,6 +76,7 @@ public class X3dOutput {
this.clt_3d_passes = clt_3d_passes;
}
// init document, bounding box, backdrop
// 09.18.2022 - made work w/o background
public void generateBackground(boolean use_backdrop)
{
try {
......@@ -104,6 +105,11 @@ public class X3dOutput {
el_TopGroup.setAttribute("bboxSize", String.format("%.3f %.3f %.3f ",bbox[1][0],bbox[1][1],bbox[1][2]));
el_Scene.appendChild(el_TopGroup);
if (clt_3d_passes == null) {
System.out.println("Not using background without clt_3d_passes");
return;
}
CLTPass3d bgnd_pass = clt_3d_passes.get(0);
......@@ -167,6 +173,10 @@ public class X3dOutput {
sb_tex_coords.append(String.format("%.4f %.4f", texCoord[i][0], texCoord[i][1]));
}
String sindex = sb_coord_index.toString(); // for both coordIndex and texCoordIndex
if (sindex.length() == 0) {
System.out.println("addCluster(): sindex.length() == 0");
System.out.println("addCluster(): sindex.length() == 0");
}
String scoord = sb_coords.toString();
String stcoord = sb_tex_coords.toString();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment