Skip to content

Instantly share code, notes, and snippets.

@kenkawakenkenke
Forked from gpavlidi/n3mtodae.c
Last active December 12, 2015 07:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kenkawakenkenke/4736541 to your computer and use it in GitHub Desktop.
Save kenkawakenkenke/4736541 to your computer and use it in GitHub Desktop.
Java port of the N3M->DAE converter
import java.awt.Desktop;
import java.io.*;
import java.net.URL;
/**
* Ported from: http://techbeyond.wordpress.com/2012/02/04/nokia-3d-webgl-map-experiments/
* Originally: https://gist.github.com/1651767
* @author ken
* 13.02.08 @kenkawakenkenke
*
*/
public class N3M_public {
static final String TILE_DOWNLOAD_URL="1.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/satellite.day/";
static final String URL_SUFFIX="jpg?token=fee2f2a877fd4a429f17207a57658582&app_id=nokiaMaps";
static final String TEXTURE_URL="maps3d.svc.nokia.com/data4";
/**
* Read methods
*/
public static float getFloat(byte buf[], int offset){
return Float.intBitsToFloat(getInt(buf,offset));
}
public static double getDouble(byte buf[], int offset){
return Double.longBitsToDouble(getLong(buf,offset));
}
public static int getInt(byte buf[], int offset){
return ((int)(0xff&buf[offset+0])<<0)
|((int)(0xff&buf[offset+1])<<8)
|((int)(0xff&buf[offset+2])<<16)
|((int)(0xff&buf[offset+3])<<24)
;
}
public static long getLong(byte buf[], int offset){
return ((long)(0xff&buf[offset+0])<<0)
|((long)(0xff&buf[offset+1])<<8)
|((long)(0xff&buf[offset+2])<<16)
|((long)(0xff&buf[offset+3])<<24)
|((long)(0xff&buf[offset+4])<<32)
|((long)(0xff&buf[offset+5])<<40)
|((long)(0xff&buf[offset+6])<<48)
|((long)(0xff&buf[offset+7])<<56)
;
}
public static int getUnsignedShort(byte buf[], int offset){
return (
((int)(0xff&buf[offset+0])<<0)
|((int)(0xff&buf[offset+1])<<8)
);
}
public static boolean downloadFile(String urlStr, File f){
if(!f.exists()){
try{
URL url=new URL(urlStr);
BufferedInputStream in=new BufferedInputStream(url.openStream());
BufferedOutputStream out=new BufferedOutputStream(new FileOutputStream(f));
byte bs[]=new byte[1024];
int length;
while((length=in.read(bs))>0)
out.write(bs, 0, length);
in.close();
out.close();
return true;
}catch(Exception e){
e.printStackTrace();
}
}
return false;
}
public static class XYZ
{
public final float x, y, z;
public XYZ(float x,float y, float z){
this.x=x;
this.y=y;
this.z=z;
}
public static XYZ fromBytes(byte buf[], int offset){
float x=getFloat(buf,offset);
float y=getFloat(buf,offset+4*1);
float z=getFloat(buf,offset+4*2);
return new XYZ(x,y,z);
}
public String toString(){
return x+","+y+","+z;
}
public XYZ crossproduct(XYZ b)
{
XYZ vector=new XYZ(this.y*b.z - b.y*this.z,
-(this.x*b.z)+(b.x*this.z),
(this.x*b.y) - (this.y*b.x)
);
return vector;
}
public XYZ normalize()
{
double magnitude = Math.sqrt(x*x+y*y+z*z);
return new XYZ(
(float)(x/magnitude),
(float) (y/magnitude),
(float) (z/magnitude));
}
public static XYZ getNormal(XYZ a,XYZ b,XYZ c)
{
//AB vector
XYZ x1=new XYZ(b.x - a.x,
b.y - a.y,
b.z - a.z) ;
//AC vector
XYZ x2=new XYZ(
c.x - a.x,
c.y - a.y,
c.z - a.z) ;
return x1.crossproduct(x2).normalize();
}
}
public static class UV
{
public UV(float u,float v){
this.u=u;
this.v=v;
}
public UV(){
}
float u, v;
public static UV fromBytes(byte buf[], int offset){
float u=getFloat(buf,offset);
float v=getFloat(buf,offset+4);
return new UV(u,v);
}
public String toString(){
return u+","+v;
}
}
public static class Triangle
{
public Triangle(int a,int b,int c){
this.a=a;
this.b=b;
this.c=c;
}
public Triangle(){
}
int a, b, c;
public static Triangle fromBytes(byte buf[], int offset){
int a=getUnsignedShort(buf,offset);
int b=getUnsignedShort(buf,offset+2*1);
int c=getUnsignedShort(buf,offset+2*2);
return new Triangle(a,b,c);
}
public String toString(){
return a+","+b+","+c;
}
}
public static class Source
{
int number_of_verts;
XYZ[] xyz_coords;
UV[] uv_coords;
}
public static class Material
{
int number_of_tris;
int source_index;
int bucket_levels ;
int bucket_count ;
String texture_name;
String texture;
boolean is_sat ;
int[] bucketOffsets ;
Triangle tris[];
};
public static class Model
{
int bucket_levels; //32
int number_of_sources;
int number_of_materials;
Source sources[];
Material materials[];
float center[]=new float[3] ;
float radius[]=new float[3] ;
float matrixFloat[]=new float[16] ;
double matrixDouble[]=new double[16] ;
public static byte[] readDataFromFile(File f){
//read data
byte data[]=null;
try{
BufferedInputStream in=(new BufferedInputStream(new FileInputStream(f)));
ByteArrayOutputStream out=new ByteArrayOutputStream();
int length;
byte buf[]=new byte[1024];
while((length=in.read(buf))>0){
out.write(buf,0,length);
}
in.close();
out.close();
data=out.toByteArray();
}catch(Exception e){
e.printStackTrace();
}
return data;
}
public Model(File f) throws Exception{
this(readDataFromFile(f));
}
public Model(byte data[]) throws Exception{
byte magic[] = data;
if (magic[0] != 'N' || magic[1] != '3' || magic[2] != 'M') {
throw new Exception("incorrect file");
}
int version = (int)(0xff&magic[3]);
boolean version2OrNewer= version>=50;
boolean version4OrNewer= version>=52;
number_of_sources = getInt(data,4*1);
number_of_materials = getInt(data,4*2);
sources = new Source[number_of_sources];
materials = new Material[number_of_materials];
bucket_levels = 32 ;
for (int i = 0; i < number_of_sources; i++) {
Source s = new Source();
sources[i]=s;
int offset = i * 2 + 3;
int data_offset = getInt(data,4*offset);
s.number_of_verts = getInt(data,4*(offset+1));
s.xyz_coords=new XYZ[s.number_of_verts];
for(int j=0;j<s.number_of_verts;j++)
s.xyz_coords[j] = XYZ.fromBytes(data,data_offset+j*(4*3));
s.uv_coords = new UV[s.number_of_verts];
for(int j=0;j<s.number_of_verts;j++)
s.uv_coords[j]=UV.fromBytes(data, (data_offset+ s.number_of_verts*12) + j*(4*2));
}
for (int i = 0; i < number_of_materials; i++) {
Material mat = new Material();
materials[i]= mat;
int offset = number_of_sources * 2 + i * 4 + 3;
int indices_offset = getInt(data,4*offset);//header[offset];
int textureNameOffset = getInt(data,4*(offset+3));//header[ offset +3 ] ; //DONT KNOW YET WHAT IT IS
mat.number_of_tris = getInt(data,4*(offset+1));//header[offset + 1];
mat.source_index = getInt(data,4*(offset+2));//header[offset + 2];
mat.tris=new Triangle[mat.number_of_tris];
for(int j=0;j<mat.number_of_tris;j++)
mat.tris[j]=Triangle.fromBytes(data, indices_offset+ j*(2*3));
if(version4OrNewer)
{
int offset2 = indices_offset + mat.number_of_tris*6 ;
mat.bucket_levels = getUnsignedShort(data, offset2) ;
if( mat.bucket_levels < bucket_levels)
bucket_levels = mat.bucket_levels ;
mat.bucket_count = 1 << (mat.bucket_levels << 1) ;
mat.bucketOffsets=new int[mat.bucket_count+1];
for(int j=0;j<mat.bucket_count;j++)
mat.bucketOffsets[j]= getUnsignedShort(data, offset2+ 2*(j+1));
mat.bucketOffsets[ mat.bucket_count ] = mat.number_of_tris ;
}
else //older than version 4 pointclouds
{
bucket_levels = 0 ;
mat.bucket_levels = 0 ;
}
int textureNameLength = (int)(0xff&magic[ textureNameOffset ]) ;
mat.texture_name=new String(magic, textureNameOffset+1, textureNameLength);
}
int center_radius_offset = getInt(data,4*(number_of_sources*2 +number_of_materials*4 +3)) ;
center[0] = getFloat(data, center_radius_offset+64+0);
center[1] = getFloat(data, center_radius_offset+64+4);
center[2] = getFloat(data, center_radius_offset+64+4*2);
radius[0] = getFloat(data, center_radius_offset+76+0);
radius[1] = getFloat(data, center_radius_offset+76+4);
radius[2] = getFloat(data, center_radius_offset+76+4*2);
if(version2OrNewer)
{
for(int i=0; i<16; i++)
matrixDouble[ i ] = getDouble(data,center_radius_offset+88+ i*8);
for(int i=0; i<16; i++)
matrixFloat[ i ] = getFloat(data,center_radius_offset+ i*4);
}
else
{
for(int i=0; i<16; i++)
matrixFloat[ i ] = getFloat(data,center_radius_offset+ i*4);
}
if (bucket_levels == 32)
bucket_levels = 0 ;
//get texture URL
for( int i=0; i< number_of_materials; i++)
{
int strLength = materials[ i ].texture_name.length() ;
//ending in sat
if( strLength>3 && materials[ i ].texture_name.endsWith("sat"))
{
materials[ i ].texture=String.format("%s%s%s", "../../tile/" , materials[ i ].texture_name , ".jpg" ) ;
materials[ i ].is_sat = true ;
}
else
{
//starting with sat_
//sat_15_20107_5242
if( strLength>3 && materials[i].texture_name.startsWith("sat_"))
{
String split[]=materials[i].texture_name.split("_");
int tilex, tiley, zoom, twoPowerZoom ;
zoom=Integer.parseInt(split[1]);
tilex=Integer.parseInt(split[2]);
tiley=Integer.parseInt(split[3]);
twoPowerZoom=(int)(0.5+Math.pow(2.0, zoom));
materials[i].texture=String.format("%s%d/%d/%d/256/%s" ,TILE_DOWNLOAD_URL, zoom, tiley, twoPowerZoom-1-tilex, URL_SUFFIX ) ;
materials[ i ].is_sat = true ;
materials[i].texture_name=String.format("%s.jpg", materials[ i ].texture_name ) ;
}
else
{
//b0.texture = bw(b2.url_dir + bW)
//maps3d.svc.nokia.com/data4/13/4914/10/
// for map_13_4918_1407_0.jpg
String split[]=materials[i].texture_name.split("_");
int tilex, tiley, zoom ;
zoom=Integer.parseInt(split[1]);
tilex=Integer.parseInt(split[2]);
tiley=Integer.parseInt(split[3]);
String urlPrefix = obfuscateURL(zoom, tilex, tiley) ;
materials[ i ].texture=String.format("%s%s", urlPrefix, materials[ i ].texture_name ) ;
materials[ i ].is_sat = false ;
}
}
}
}
public boolean downloadTextures(File dir)
{
for( int i=0; i< number_of_materials; i++)
{
File outFile=new File(dir,materials[i].texture_name);
if(!outFile.exists()){
String urlStr="http://"+materials[i].texture;
if(!downloadFile(urlStr, outFile))
return false;
}
}
return true;
}
public void saveAsDAE (File f) throws FileNotFoundException
{
PrintWriter out=new PrintWriter(f);
out.printf( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
out.printf( "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">\n");
out.printf( " <asset>\n");
out.printf( " <contributor>\n");
out.printf( " <authoring_tool>n3mtodae</authoring_tool>\n");
out.printf( " </contributor>\n");
out.printf( " <created>2012-01-21T04:10:34Z</created>\n");
out.printf( " <modified>2012-01-21T04:10:34Z</modified>\n");
out.printf( " <unit meter=\"0.0254000\" name=\"inch\" />\n");
out.printf( " <up_axis>X_UP</up_axis>\n");
out.printf( " </asset>\n");
//IMPORT TEXTURE IMAGES
out.printf( " <library_images>");
int i = 0;
for (i = 0; i < number_of_materials; i++)
{
Material mat = materials[i];
out.printf( "\n <image id=\"material%d-image\" name=\"material%d-image\">", mat.hashCode(), mat.hashCode());
out.printf( "\n <init_from>%s</init_from>", mat.texture_name);
out.printf( "\n </image>");
}
out.printf( "\n </library_images>\n");
//IMPORT MATERIALS
out.printf( " <library_materials>");
i = 0;
for (i = 0; i < number_of_materials; i++)
{
Material mat = materials[i];
out.printf( "\n <material id=\"material%dID\" name=\"material%d\">", mat.hashCode(), mat.hashCode());
out.printf( "\n <instance_effect url=\"#material%d-effect\" />", mat.hashCode());
out.printf( "\n </material>");
}
out.printf( "\n </library_materials>\n");
//DECLARE EFFECTS
out.printf( " <library_effects>\n");
i = 0;
for (i = 0; i < number_of_materials; i++)
{
Material mat = materials[i];
out.printf( " <effect id=\"material%d-effect\" name=\"material%d-effect\">\n", mat.hashCode(), mat.hashCode());
out.printf( " <profile_COMMON>\n");
out.printf( " <newparam sid=\"material%d-image-surface\">\n", mat.hashCode());
out.printf( " <surface type=\"2D\">\n");
out.printf( " <init_from>material%d-image</init_from>\n", mat.hashCode());
out.printf( " </surface>\n");
out.printf( " </newparam>\n");
out.printf( " <newparam sid=\"material%d-image-sampler\">\n", mat.hashCode());
out.printf( " <sampler2D>\n");
out.printf( " <source>material%d-image-surface</source>\n", mat.hashCode());
out.printf( " </sampler2D>\n");
out.printf( " </newparam>\n");
out.printf( " <technique sid=\"COMMON\">\n");
out.printf( " <lambert>\n");
out.printf( " <emission>\n");
out.printf( " <color>0.0000000 0.0000000 0.0000000 1.0000000</color>\n");
out.printf( " </emission>\n");
out.printf( " <ambient>\n");
out.printf( " <color>0.0000000 0.0000000 0.0000000 1.0000000</color>\n");
out.printf( " </ambient>\n");
out.printf( " <diffuse>\n");
out.printf( " <texture texture=\"material%d-image-sampler\" texcoord=\"UVSET0\"/>\n", mat.hashCode());
out.printf( " </diffuse>\n");
out.printf( " </lambert>\n");
out.printf( " </technique>\n");
out.printf( " </profile_COMMON>\n");
out.printf( " </effect>\n");
}
out.printf( " </library_effects>\n");
out.printf( " <library_visual_scenes>\n");
out.printf( " <visual_scene id=\"ID1VISUALSCENE\">\n");
out.printf( " <node name=\"n3mtodae\">\n");
i = 0;
for (i = 0; i < number_of_materials; i++) {
Material mat = materials[i];
out.printf( " <instance_geometry url=\"#ID%dGEOMETRY\">\n", mat.hashCode());
out.printf( " <bind_material>\n");
out.printf( " <technique_common>\n");
out.printf( " <instance_material symbol=\"material%d\" target=\"#material%dID\">\n", mat.hashCode(), mat.hashCode());
out.printf( " <bind_vertex_input semantic=\"UVSET0\" input_semantic=\"TEXCOORD\" input_set=\"0\" />\n");
out.printf( " </instance_material>\n");
out.printf( " </technique_common>\n");
out.printf( " </bind_material>\n");
out.printf( " </instance_geometry>\n");
}
out.printf( " </node>\n");
out.printf( " </visual_scene>\n");
out.printf( " </library_visual_scenes>\n");
out.printf( " <library_geometries>\n");
for (i = 0; i < number_of_materials; i++) {
Material mat = materials[i];
Source src = sources[mat.source_index];
out.printf( " <geometry id=\"ID%dGEOMETRY\">\n", mat.hashCode());
out.printf( " <mesh>\n");
//Write geometry position
out.printf( " <source id=\"ID%dPOSITION\">\n", src.xyz_coords.hashCode());
out.printf( " <float_array id=\"ID%dPOSITION-ARRAY\" count=\"%d\">", src.xyz_coords.hashCode(), src.number_of_verts * 3);
int j = 0;
for (j = 0; j < src.number_of_verts; j++) {
out.printf( "%g %g %g ", src.xyz_coords[j].x, src.xyz_coords[j].y, src.xyz_coords[j].z);
}
out.printf( "</float_array>\n");
out.printf( " <technique_common>\n");
out.printf( " <accessor count=\"%d\" source=\"#ID%dPOSITION-ARRAY\" stride=\"3\">\n", src.number_of_verts, src.xyz_coords.hashCode());
out.printf( " <param name=\"X\" type=\"float\" />\n");
out.printf( " <param name=\"Y\" type=\"float\" />\n");
out.printf( " <param name=\"Z\" type=\"float\" />\n");
out.printf( " </accessor>\n");
out.printf( " </technique_common>\n");
out.printf( " </source>\n");
//Write geometry normals
//3 normals per triangle
out.printf( " <source id=\"ID%dNORMALS\">\n", src.xyz_coords.hashCode());
out.printf( " <float_array id=\"ID%dNORMALS-ARRAY\" count=\"%d\">", src.xyz_coords.hashCode(), mat.number_of_tris * 3);
j = 0;
for (j = 0; j < mat.number_of_tris; j++) {
XYZ normal = XYZ.getNormal( src.xyz_coords[ mat.tris[j].a ], src.xyz_coords[ mat.tris[j].b ], src.xyz_coords[ mat.tris[j].c ] ) ;
out.printf( "%g %g %g ", normal.x, normal.y, normal.z);
}
out.printf( "</float_array>\n");
out.printf( " <technique_common>\n");
out.printf( " <accessor count=\"%d\" source=\"#ID%dNORMALS-ARRAY\" stride=\"3\">\n", mat.number_of_tris, src.xyz_coords.hashCode());
out.printf( " <param name=\"X\" type=\"float\" />\n");
out.printf( " <param name=\"Y\" type=\"float\" />\n");
out.printf( " <param name=\"Z\" type=\"float\" />\n");
out.printf( " </accessor>\n");
out.printf( " </technique_common>\n");
out.printf( " </source>\n");
//Write geometry UV
out.printf( " <source id=\"ID%dUV\">\n", src.xyz_coords.hashCode());
out.printf( " <float_array id=\"ID%dUV-ARRAY\" count=\"%d\">", src.xyz_coords.hashCode(), src.number_of_verts * 2);
j = 0;
for (j = 0; j < src.number_of_verts; j++) {
out.printf( "%g %g ", src.uv_coords[j].u, src.uv_coords[j].v);
}
out.printf( "</float_array>\n");
out.printf( " <technique_common>\n");
out.printf( " <accessor count=\"%d\" source=\"#ID%dUV-ARRAY\" stride=\"2\">\n", src.number_of_verts, src.xyz_coords.hashCode());
out.printf( " <param name=\"S\" type=\"float\" />\n");
out.printf( " <param name=\"T\" type=\"float\" />\n");
out.printf( " </accessor>\n");
out.printf( " </technique_common>\n");
out.printf( " </source>\n");
out.printf( " <vertices id=\"ID%dVERTICES\">\n", mat.hashCode());
out.printf( " <input semantic=\"POSITION\" source=\"#ID%dPOSITION\" />\n", src.xyz_coords.hashCode());
out.printf( " </vertices>\n");
out.printf( " <triangles count=\"%d\" material=\"material%d\">\n", mat.number_of_tris, mat.hashCode());
out.printf( " <input offset=\"0\" semantic=\"VERTEX\" source=\"#ID%dVERTICES\" />\n", mat.hashCode());
out.printf( " <input offset=\"1\" semantic=\"NORMAL\" source=\"#ID%dNORMALS\" />\n", mat.hashCode());
out.printf( " <input offset=\"2\" set=\"0\" semantic=\"TEXCOORD\" source=\"#ID%dUV\" />\n", src.xyz_coords.hashCode());
out.printf( " <p>");
for (j = 0; j < mat.number_of_tris; j++) {
out.printf( "%d %d %d %d %d %d %d %d %d ", mat.tris[j].a, j, mat.tris[j].a, mat.tris[j].b, j, mat.tris[j].b, mat.tris[j].c, j, mat.tris[j].c);
}
out.printf( "</p>\n");
out.printf( " </triangles>\n");
out.printf( " </mesh>\n");
out.printf( " </geometry>\n");
}
out.printf( " </library_geometries>\n");
out.printf( " <scene>\n");
out.printf( " <instance_visual_scene url=\"#ID1VISUALSCENE\" />\n");
out.printf( " </scene>\n");
out.printf( "</COLLADA>\n");
out.close();
}
public static String obfuscateURL(int zoom, int tilex, int tiley)
{
int bz = (int)(Math.floor(((zoom * 302 + 1000) / 1000.) )+0.5) ;
String bs=String.format("%s/%d", TEXTURE_URL, zoom) ;
//number of zeros
int numOfZeros = bz - (""+tilex).length();
String tempx="";
for(int i=0; i< numOfZeros; i++)
tempx+='0' ;
tempx+=tilex;
numOfZeros = bz - (""+tiley).length();
String tempy="";
for(int i=0; i< numOfZeros; i++)
tempy+="0";
tempy+=tiley;
int bx ;
for (bx = 0; bx < bz - 2; bx += 2)
{
bs+='/' ;
bs+=tempx.charAt(bx);
bs+=tempx.charAt(bx + 1);
bs+=tempy.charAt(bx);
bs+= tempy.charAt(bx + 1) ;
}
for (; bx < bz - 1; bx++)
{
bs+='/';
bs+=tempx.charAt(bx);
bs+=tempy.charAt(bx);
}
bs+='/';
return bs ;
}
}
public static File getN3MFor(File baseDir,TileSpec spec){
File file=new File(baseDir,spec.mapName()+".n3m");
if(!file.exists()){
int first= 100*((spec.y/10000)%100)+ (spec.x/10000)%100;
int second= 100*((spec.y/100)%100)+ (spec.x/100)%100;
int third= 10*((spec.y/10)%10)+ (spec.x/10)%10;
String urlStr="http://c.maps3d.svc.nokia.com/data4//"+spec.level+"/"+(first!=0?(String.format("%04d",first)+"/"):"")+(second!=0?(String.format("%04d",second)+"/"):"")+(third!=0?(String.format("%02d",third)+"/"):"")+"map_"+spec.level+"_"+spec.y+"_"+spec.x+".n3m";
if(downloadFile(urlStr, file))
return file;
return null;
}
return file;
}
public static File loadDAE(File baseDir, TileSpec spec) throws Exception{
File dir=new File(baseDir,spec.mapName());
if(!dir.exists())
dir.mkdir();
File f=getN3MFor(dir,spec);
if(f!=null){
System.out.println(f);
Model m=new Model(f);
m.downloadTextures(dir);
File outFile=new File(dir,f.getName()+".dae");
m.saveAsDAE(outFile);
File okFile=new File(dir,"ok");
if(okFile.exists() || okFile.createNewFile())
System.out.println(" -> exported to "+outFile);
return outFile;
}
return null;
}
public static class TileSpec{
public final int level;
public final int x,y;
public static TileSpec fromMapName(String mapName){
String split[]=mapName.split("_|\\.");
int level=Integer.parseInt(split[1]);
int x=Integer.parseInt(split[3]);
int y=Integer.parseInt(split[2]);
return new TileSpec(level, x, y);
}
public static TileSpec fromURLStr(String urlStr){
String split[]=urlStr.split("/");
return fromMapName(split[split.length-1]);
}
public TileSpec(int level, int x,int y){
this.level=level;
this.x=x;
this.y=y;
}
public String toString(){
return level+","+y+","+x;
}
public String mapName(){
return "map_"+level+"_"+y+"_"+x;
}
}
public static void main(String[]args) throws Exception
{
TileSpec spec=null;
{
int level=19;
int x=261956;
int y=349934;
spec=new TileSpec(level, x, y);
}
File baseDir=new File("buildings");
if(!baseDir.exists())
baseDir.mkdir();
File outFile=loadDAE(baseDir,spec);
Desktop.getDesktop().open(outFile);
}
}
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include <Share.h>
#include <math.h>
#define TILE_DOWNLOAD_URL "1.maptile.lbs.ovi.com/maptiler/v2/maptile/newest/satellite.day/"
#define URL_SUFFIX "jpg?token=fee2f2a877fd4a429f17207a57658582&app_id=nokiaMaps"
#define TEXTURE_URL "maps3d.svc.nokia.com/data4"
struct xyz
{
float x, y, z;
};
struct uv
{
float u, v;
};
struct triangle
{
unsigned short a, b, c;
};
struct source
{
int number_of_verts;
struct xyz *xyz_coords;
struct uv *uv_coords;
};
struct material
{
int number_of_tris;
int source_index;
int bucket_levels ;
int bucket_count ;
char texture_name[300] ;
char texture[500] ;
bool is_sat ;
unsigned short *bucketOffsets ;
struct triangle *tris;
};
struct model
{
int bucket_levels; //32
int number_of_sources;
int number_of_materials;
struct source *sources;
struct material *materials;
float center[3] ;
float radius[3] ;
float matrixFloat[16] ;
double matrixDouble[16] ;
};
bool version2OrNewer = false ;
bool version4OrNewer = false ;
struct xyz crossproduct(struct xyz a, struct xyz b)
{
struct xyz vector;
vector.x = a.y*b.z - b.y*a.z ; //(Ay*Bz)-(By*Az);
vector.y = -(a.x*b.z)+(b.x*a.z) ; //-(Ax*Bz)+(Bx*Az);
vector.z = (a.x*b.y) - (a.y*b.x) ; //(Ax*By)-(Ay*Bx);
return vector;
}
struct xyz normalize(struct xyz a)
{
struct xyz out ;
double magnitude = sqrt(a.x*a.x+a.y*a.y+a.z*a.z);
out.x = a.x/magnitude ;
out.y = a.y/magnitude ;
out.z = a.z/magnitude ;
return out ;
}
struct xyz getNormal(struct xyz a, struct xyz b, struct xyz c)
{
struct xyz normal, x1, x2 ;
//AB vector
x1.x = b.x - a.x ; x1.y = b.y - a.y ; x1.z = b.z - a.z ;
//AC vector
x2.x = c.x - a.x ; x2.y = c.y - a.y ; x2.z = c.z - a.z ;
normal = crossproduct( x1, x2) ;
return normalize( normal ) ;
}
int getNumDecimalDigits(int value)
{
char myString[50];
sprintf(myString,"%d",value);
return strlen(myString);
}
char * obfuscateURL(int zoom, int tilex, int tiley)
{
int bz = floor( float((zoom * 302 + 1000) / 1000) ) ;
char bs[500], tempx[500], tempy[500] ;
sprintf(bs, "%s/%d", TEXTURE_URL, zoom) ;
//number of zeros
int numOfZeros = bz - getNumDecimalDigits(tilex) ;
for(int i=0; i< numOfZeros; i++)
tempx[i] = '0' ;
sprintf( &tempx[numOfZeros], "%d", tilex) ;
numOfZeros = bz - getNumDecimalDigits(tiley) ;
for(int i=0; i< numOfZeros; i++)
tempy[i] = '0' ;
sprintf( &tempy[numOfZeros], "%d", tiley) ;
int stringLength = strlen( bs ) ;
int bx ;
for (bx = 0; bx < bz - 2; bx += 2)
{
bs[stringLength++ ] = '/' ;
bs[stringLength++ ] = tempx[bx];
bs[stringLength++ ] = tempx[bx + 1];
bs[stringLength++ ] = tempy[bx];
bs[stringLength++ ] = tempy[bx + 1] ;
}
for (; bx < bz - 1; bx++)
{
bs[stringLength++ ] = '/';
bs[stringLength++ ] = tempx[bx];
bs[stringLength++ ] = tempy[bx];
}
bs[stringLength++ ] = '/';
bs[stringLength] = '\0';
return bs ;
}
void parseTextureURLs( struct model *m )
{
for( int i=0; i< m->number_of_materials; i++)
{
int strLength = strlen( m->materials[ i ].texture_name ) ;
//ending in sat
if( strLength>3 && m->materials[ i ].texture_name[ strLength - 3]=='s' && m->materials[ i ].texture_name[ strLength - 2]=='a' && m->materials[ i ].texture_name[ strLength - 1]=='t')
{
sprintf( m->materials[ i ].texture, "%s%s%s", "../../tile/" , m->materials[ i ].texture_name , ".jpg" ) ;
m->materials[ i ].is_sat = true ;
}
else
{
//starting with sat_
//sat_15_20107_5242
if( strLength>3 && m->materials[ i ].texture_name[0]=='s' && m->materials[ i ].texture_name[1]=='a' && m->materials[ i ].texture_name[ 2]=='t' && m->materials[ i ].texture_name[ 3]=='_')
{
char temp[50] ;
int offset = 4 ;
int tilex, tiley, zoom, twoPowerZoom ;
int tokenIndex = strchr(&(m->materials[ i ].texture_name[offset]) , '_') - &(m->materials[ i ].texture_name[offset]) ;
strncpy(temp, &(m->materials[ i ].texture_name[offset]), tokenIndex);
temp[ tokenIndex] = '\0' ;
zoom = atoi(temp) ;
offset = offset + tokenIndex + 1 ;
tokenIndex = strchr(&(m->materials[ i ].texture_name[offset]) , '_') - &(m->materials[ i ].texture_name[offset]) ;
strncpy(temp, &(m->materials[ i ].texture_name[offset]), tokenIndex);
temp[ tokenIndex] = '\0' ;
tilex = atoi(temp) ;
offset = offset + tokenIndex + 1 ;
strncpy(temp, &(m->materials[ i ].texture_name[offset]), strLength - offset);
temp[ strLength - offset ] = '\0' ;
tiley = atoi(temp) ;
twoPowerZoom = pow(2.0, zoom) ;
sprintf( m->materials[ i ].texture, "%s%d/%d/%d/256/%s" ,TILE_DOWNLOAD_URL, zoom, tiley, twoPowerZoom-1-tilex, URL_SUFFIX ) ;
m->materials[ i ].is_sat = true ;
sprintf( m->materials[ i ].texture_name, "%s.jpg", m->materials[ i ].texture_name ) ;
}
else
{
//b0.texture = bw(b2.url_dir + bW)
//maps3d.svc.nokia.com/data4/13/4914/10/
// for map_13_4918_1407_0.jpg
char *urlPrefix ;
char temp[50] ;
int offset = 4 ;
int tilex, tiley, zoom, twoPowerZoom ;
int tokenIndex = strchr(&(m->materials[ i ].texture_name[offset]) , '_') - &(m->materials[ i ].texture_name[offset]) ;
strncpy(temp, &(m->materials[ i ].texture_name[offset]), tokenIndex);
temp[ tokenIndex] = '\0' ;
zoom = atoi(temp) ;
offset = offset + tokenIndex + 1 ;
tokenIndex = strchr(&(m->materials[ i ].texture_name[offset]) , '_') - &(m->materials[ i ].texture_name[offset]) ;
strncpy(temp, &(m->materials[ i ].texture_name[offset]), tokenIndex);
temp[ tokenIndex] = '\0' ;
tilex = atoi(temp) ;
offset = offset + tokenIndex + 1 ;
tokenIndex = strchr(&(m->materials[ i ].texture_name[offset]) , '_') - &(m->materials[ i ].texture_name[offset]) ;
strncpy(temp, &(m->materials[ i ].texture_name[offset]), tokenIndex);
temp[ tokenIndex] = '\0' ;
tiley = atoi(temp) ;
urlPrefix = obfuscateURL(zoom, tilex, tiley) ;
sprintf( m->materials[ i ].texture, "%s%s", urlPrefix, m->materials[ i ].texture_name ) ;
m->materials[ i ].is_sat = false ;
}
}
}
}
int open_n3m (struct model *m, void *data)
{
char *magic = (char*)data;
printf("magic: %c%c%c\n", magic[0], magic[1], magic[2]);
if (magic[0] != 'N' || magic[1] != '3' || magic[2] != 'M') {
return -1;
}
char version = magic[3];
if( version >= 50 )
version2OrNewer = true ;
if( version >= 52 )
version4OrNewer = true ;
printf("version: %c\n", version);
int *header = (int*)data;
m->number_of_sources = header[1];
m->number_of_materials = header[2];
printf("number_of_sources: %d\n", m->number_of_sources);
printf("number_of_materials: %d\n", m->number_of_materials);
m->sources = (source *) calloc(m->number_of_sources, sizeof(struct source));
m->materials = (material *) calloc(m->number_of_materials, sizeof(struct material));
m->bucket_levels = 32 ;
int i = 0;
for (i = 0; i < m->number_of_sources; i++) {
struct source *s = &m->sources[i];
int offset = i * 2 + 3;
int data_offset = header[offset];
s->number_of_verts = header[offset + 1];
printf("data_offset: %d\n", data_offset);
printf("source[%d].number_of_verts: %d\n", i, s->number_of_verts);
s->xyz_coords = (struct xyz*)((char*)data + data_offset); //number_of_verts*3
s->uv_coords = (struct uv*)((char*)data + data_offset + s->number_of_verts * 12); //number_of_verts*2
}
for (i = 0; i < m->number_of_materials; i++) {
struct material *mat = &m->materials[i];
int offset = m->number_of_sources * 2 + i * 4 + 3;
int indices_offset = header[offset];
int textureNameOffset = header[ offset +3 ] ; //DONT KNOW YET WHAT IT IS
mat->number_of_tris = header[offset + 1];
mat->source_index = header[offset + 2];
mat->tris = (struct triangle*)((char*)data + indices_offset); //number_of_tris *3
printf("material[%d].number_of_tris: %d\n", i, mat->number_of_tris);
printf("material[%d].source_index: %d\n", i, mat->source_index);
if(version4OrNewer)
{
int offset2 = indices_offset + mat->number_of_tris*6 ;
unsigned short *matrix = (unsigned short*)( (char*)data + offset2);
mat->bucket_levels = matrix[0] ;
if( mat->bucket_levels < m->bucket_levels)
m->bucket_levels = mat->bucket_levels ;
mat->bucket_count = 1 << (mat->bucket_levels << 1) ;
mat->bucketOffsets = (unsigned short *) calloc(mat->bucket_count + 1, sizeof(unsigned short));
for( int i=0; i< mat->bucket_count; i++ )
mat->bucketOffsets[ i ] = matrix[ i+1 ] ;
mat->bucketOffsets[ mat->bucket_count ] = mat->number_of_tris ;
}
else //older than version 4 pointclouds
{
m->bucket_levels = 0 ;
mat->bucket_levels = 0 ;
}
unsigned char textureNameLength = magic[ textureNameOffset ] ;
for(int i=0; i< textureNameLength; i++)
mat->texture_name[i] = magic[ textureNameOffset +1 + i] ;
mat->texture_name[textureNameLength] = '\0' ;
}
int center_radius_offset = header[ m->number_of_sources*2 + m->number_of_materials*4 +3] ;
m->center[0] = ((float*)( (char*)data + center_radius_offset +64 ))[0] ;
m->center[1] = ((float*)( (char*)data + center_radius_offset +64 ))[1] ;
m->center[2] = ((float*)( (char*)data + center_radius_offset +64 ))[2] ;
m->radius[0] = ((float*)( (char*)data + center_radius_offset +76 ))[0] ;
m->radius[1] = ((float*)( (char*)data + center_radius_offset +76 ))[1] ;
m->radius[2] = ((float*)( (char*)data + center_radius_offset +76 ))[2] ;
if(version2OrNewer)
{
for(int i=0; i<16; i++)
m->matrixDouble[ i ] = ((double*)( (char*)data + center_radius_offset +88 ))[i] ;
for(int i=0; i<16; i++)
m->matrixFloat[ i ] = ((float*)( (char*)data + center_radius_offset ))[i] ;
}
else
{
for(int i=0; i<16; i++)
m->matrixFloat[ i ] = ((float*)( (char*)data + center_radius_offset ))[i] ;
}
if (m->bucket_levels == 32)
m->bucket_levels = 0 ;
return 0;
}
int save_dae (struct model *m, FILE *f)
{
struct xyz normal ;
fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
fprintf(f, "<COLLADA xmlns=\"http://www.collada.org/2005/11/COLLADASchema\" version=\"1.4.1\">\n");
fprintf(f, " <asset>\n");
fprintf(f, " <contributor>\n");
fprintf(f, " <authoring_tool>n3mtodae</authoring_tool>\n");
fprintf(f, " </contributor>\n");
fprintf(f, " <created>2012-01-21T04:10:34Z</created>\n");
fprintf(f, " <modified>2012-01-21T04:10:34Z</modified>\n");
fprintf(f, " <unit meter=\"0.0254000\" name=\"inch\" />\n");
fprintf(f, " <up_axis>X_UP</up_axis>\n");
fprintf(f, " </asset>\n");
//IMPORT TEXTURE IMAGES
fprintf(f, " <library_images>");
int i = 0;
for (i = 0; i < m->number_of_materials; i++)
{
struct material *mat = &m->materials[i];
fprintf(f, "\n <image id=\"material%p-image\" name=\"material%p-image\">", mat, mat);
fprintf(f, "\n <init_from>%s</init_from>", mat->texture_name);
fprintf(f, "\n </image>");
}
fprintf(f, "\n </library_images>\n");
//IMPORT MATERIALS
fprintf(f, " <library_materials>");
i = 0;
for (i = 0; i < m->number_of_materials; i++)
{
struct material *mat = &m->materials[i];
fprintf(f, "\n <material id=\"material%pID\" name=\"material%p\">", mat, mat);
fprintf(f, "\n <instance_effect url=\"#material%p-effect\" />", mat);
fprintf(f, "\n </material>");
}
fprintf(f, "\n </library_materials>\n");
//DECLARE EFFECTS
fprintf(f, " <library_effects>\n");
i = 0;
for (i = 0; i < m->number_of_materials; i++)
{
struct material *mat = &m->materials[i];
fprintf(f, " <effect id=\"material%p-effect\" name=\"material%p-effect\">\n", mat, mat);
fprintf(f, " <profile_COMMON>\n");
fprintf(f, " <newparam sid=\"material%p-image-surface\">\n", mat);
fprintf(f, " <surface type=\"2D\">\n");
fprintf(f, " <init_from>material%p-image</init_from>\n", mat);
fprintf(f, " </surface>\n");
fprintf(f, " </newparam>\n");
fprintf(f, " <newparam sid=\"material%p-image-sampler\">\n", mat);
fprintf(f, " <sampler2D>\n");
fprintf(f, " <source>material%p-image-surface</source>\n", mat);
fprintf(f, " </sampler2D>\n");
fprintf(f, " </newparam>\n");
fprintf(f, " <technique sid=\"COMMON\">\n");
fprintf(f, " <lambert>\n");
fprintf(f, " <emission>\n");
fprintf(f, " <color>0.0000000 0.0000000 0.0000000 1.0000000</color>\n");
fprintf(f, " </emission>\n");
fprintf(f, " <ambient>\n");
fprintf(f, " <color>0.0000000 0.0000000 0.0000000 1.0000000</color>\n");
fprintf(f, " </ambient>\n");
fprintf(f, " <diffuse>\n");
fprintf(f, " <texture texture=\"material%p-image-sampler\" texcoord=\"UVSET0\"/>\n", mat);
fprintf(f, " </diffuse>\n");
fprintf(f, " </lambert>\n");
fprintf(f, " </technique>\n");
fprintf(f, " </profile_COMMON>\n");
fprintf(f, " </effect>\n");
}
fprintf(f, " </library_effects>\n");
fprintf(f, " <library_visual_scenes>\n");
fprintf(f, " <visual_scene id=\"ID1VISUALSCENE\">\n");
fprintf(f, " <node name=\"n3mtodae\">\n");
i = 0;
for (i = 0; i < m->number_of_materials; i++) {
struct material *mat = &m->materials[i];
fprintf(f, " <instance_geometry url=\"#ID%pGEOMETRY\">\n", mat);
fprintf(f, " <bind_material>\n");
fprintf(f, " <technique_common>\n");
fprintf(f, " <instance_material symbol=\"material%p\" target=\"#material%pID\">\n", mat, mat);
fprintf(f, " <bind_vertex_input semantic=\"UVSET0\" input_semantic=\"TEXCOORD\" input_set=\"0\" />\n");
fprintf(f, " </instance_material>\n");
fprintf(f, " </technique_common>\n");
fprintf(f, " </bind_material>\n");
fprintf(f, " </instance_geometry>\n");
}
fprintf(f, " </node>\n");
fprintf(f, " </visual_scene>\n");
fprintf(f, " </library_visual_scenes>\n");
fprintf(f, " <library_geometries>\n");
for (i = 0; i < m->number_of_materials; i++) {
struct material *mat = &m->materials[i];
struct source *src = &m->sources[mat->source_index];
fprintf(f, " <geometry id=\"ID%pGEOMETRY\">\n", mat);
fprintf(f, " <mesh>\n");
//Write geometry position
fprintf(f, " <source id=\"ID%pPOSITION\">\n", src->xyz_coords);
fprintf(f, " <float_array id=\"ID%pPOSITION-ARRAY\" count=\"%d\">", src->xyz_coords, src->number_of_verts * 3);
int j = 0;
for (j = 0; j < src->number_of_verts; j++) {
fprintf(f, "%g %g %g ", src->xyz_coords[j].x, src->xyz_coords[j].y, src->xyz_coords[j].z);
}
fprintf(f, "</float_array>\n");
fprintf(f, " <technique_common>\n");
fprintf(f, " <accessor count=\"%d\" source=\"#ID%pPOSITION-ARRAY\" stride=\"3\">\n", src->number_of_verts, src->xyz_coords);
fprintf(f, " <param name=\"X\" type=\"float\" />\n");
fprintf(f, " <param name=\"Y\" type=\"float\" />\n");
fprintf(f, " <param name=\"Z\" type=\"float\" />\n");
fprintf(f, " </accessor>\n");
fprintf(f, " </technique_common>\n");
fprintf(f, " </source>\n");
//Write geometry normals
//3 normals per triangle
fprintf(f, " <source id=\"ID%pNORMALS\">\n", src->xyz_coords);
fprintf(f, " <float_array id=\"ID%pNORMALS-ARRAY\" count=\"%d\">", src->xyz_coords, mat->number_of_tris * 3);
j = 0;
for (j = 0; j < mat->number_of_tris; j++) {
normal = getNormal( src->xyz_coords[ mat->tris[j].a ], src->xyz_coords[ mat->tris[j].b ], src->xyz_coords[ mat->tris[j].c ] ) ;
fprintf(f, "%g %g %g ", normal.x, normal.y, normal.z);
}
fprintf(f, "</float_array>\n");
fprintf(f, " <technique_common>\n");
fprintf(f, " <accessor count=\"%d\" source=\"#ID%pNORMALS-ARRAY\" stride=\"3\">\n", mat->number_of_tris, src->xyz_coords);
fprintf(f, " <param name=\"X\" type=\"float\" />\n");
fprintf(f, " <param name=\"Y\" type=\"float\" />\n");
fprintf(f, " <param name=\"Z\" type=\"float\" />\n");
fprintf(f, " </accessor>\n");
fprintf(f, " </technique_common>\n");
fprintf(f, " </source>\n");
//Write geometry UV
fprintf(f, " <source id=\"ID%pUV\">\n", src->xyz_coords);
fprintf(f, " <float_array id=\"ID%pUV-ARRAY\" count=\"%d\">", src->xyz_coords, src->number_of_verts * 2);
j = 0;
for (j = 0; j < src->number_of_verts; j++) {
fprintf(f, "%g %g ", src->uv_coords[j].u, src->uv_coords[j].v);
}
fprintf(f, "</float_array>\n");
fprintf(f, " <technique_common>\n");
fprintf(f, " <accessor count=\"%d\" source=\"#ID%pUV-ARRAY\" stride=\"2\">\n", src->number_of_verts, src->xyz_coords);
fprintf(f, " <param name=\"S\" type=\"float\" />\n");
fprintf(f, " <param name=\"T\" type=\"float\" />\n");
fprintf(f, " </accessor>\n");
fprintf(f, " </technique_common>\n");
fprintf(f, " </source>\n");
fprintf(f, " <vertices id=\"ID%pVERTICES\">\n", mat);
fprintf(f, " <input semantic=\"POSITION\" source=\"#ID%pPOSITION\" />\n", src->xyz_coords);
fprintf(f, " </vertices>\n");
fprintf(f, " <triangles count=\"%d\" material=\"material%p\">\n", mat->number_of_tris, mat);
fprintf(f, " <input offset=\"0\" semantic=\"VERTEX\" source=\"#ID%pVERTICES\" />\n", mat);
fprintf(f, " <input offset=\"1\" semantic=\"NORMAL\" source=\"#ID%pNORMALS\" />\n", mat);
fprintf(f, " <input offset=\"2\" set=\"0\" semantic=\"TEXCOORD\" source=\"#ID%pUV\" />\n", src->xyz_coords);
fprintf(f, " <p>");
for (j = 0; j < mat->number_of_tris; j++) {
fprintf(f, "%d %d %d %d %d %d %d %d %d ", mat->tris[j].a, j, mat->tris[j].a, mat->tris[j].b, j, mat->tris[j].b, mat->tris[j].c, j, mat->tris[j].c);
}
fprintf(f, "</p>\n");
fprintf(f, " </triangles>\n");
fprintf(f, " </mesh>\n");
fprintf(f, " </geometry>\n");
}
fprintf(f, " </library_geometries>\n");
fprintf(f, " <scene>\n");
fprintf(f, " <instance_visual_scene url=\"#ID1VISUALSCENE\" />\n");
fprintf(f, " </scene>\n");
fprintf(f, "</COLLADA>\n");
return 0;
}
void downloadTextures( model *m )
{
char cmd[600] ;
for( int i=0; i< m->number_of_materials; i++)
{
sprintf( cmd, "C:\\wget\\wget.exe -O %s http://%s", m->materials[i].texture_name, m->materials[i].texture ) ;
system(cmd) ;
}
}
int main()
{
//int fd ; //= open("/Users/fak/Desktop/Nokia3D/b.n3m", O_RDONLY);
//_sopen_s( &fd, "f_stat.out", O_RDONLY) ;
int fd = _sopen("C:/maps3D/map_19_327120_154308.n3m", O_RDONLY, _SH_DENYNO);
struct stat sb = {0};
fstat(fd, &sb);
//void *data = mmap(0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
HANDLE fm;
HANDLE h = (HANDLE) _get_osfhandle (fd);
fm = CreateFileMapping( h, NULL, PAGE_READONLY, 0,0, NULL);
void *data = static_cast<void*>(MapViewOfFile( fm, FILE_MAP_READ, 0, 0, sb.st_size));
struct model m;
open_n3m (&m, data);
parseTextureURLs( &m ) ;
downloadTextures( &m ) ;
FILE *out = fopen("C:/maps3D/map_19_327120_154308.dae", "w");
save_dae (&m, out);
UnmapViewOfFile(data);
CloseHandle(fm);
return 0;
}
@j6k4m8
Copy link

j6k4m8 commented Aug 1, 2013

Note to others in the future: Some errors may be caused by the Desktop.getDesktop() line. Comment it out if it gives you trouble!

(Still trying to figure out the mapping system for x and y to coordinates though!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment