本人因为公司需求,需要做个VR全景功能,所以研究VR功能。在网上查询了一些资料以后发现krpano软件可以满足这个需求,果断学习之,如果大家有兴趣的话可以看下这些内容:
krpano360,http://www.krpano360.com 这个网站,我是从入门教程看到初级教程,在此先感谢下网站所有人“肥宗”哥,感谢他的无私分享,如果你看完了我说的内容的话,基本也了解了krpano基本功能。
分享下krpano的下载地址:https://pan.baidu.com/s/1oAaHjB0 密码:j6w8
关于注册码,可以加我微信或者留言给我,我私信给你。
好了,言归正传。下面我们开始讲解如何krpano功能化,首先我也是参考了cdsn的大神分享的文章,在此先谢谢“J_小浩子”的文章,地址:http://blog.csdn.net/change_on/article/details/77373644,有兴趣可以先看下。
说说如何功能话krpano的思路: 先来看下krpano的文件夹结构,根目录是vtour
这是最后生成好的文件,来说明下吧,panos-放置全景图或者缩略图文件夹,plugins-公用的插件文件夹,skin-皮肤以及一些配置文件夹,tour.html-主访问页面,tour.js-js文件用户html引用,tour.swf-用于flash引用,tour.xml-主配置文件。关于具体思路请参考小浩子大神的文章,本人不在这里重复阐述,说下最后我的结果文件夹内容:
这是最后的输出文件夹内容,其中plugin,skin和tour.html是公用的,target文件夹是每次上传都不同的。
主要是在大神们的思路和代码上进行了一些代码优化,分离了方法代码和执行代码,增加了背景音乐,底部logo和载入进度条等功能。
下面我们来看下具体的实现代码:
首先是:krpano类封装
/**
*
*/
package krpano;
/**
* krpano类封装
* @author Administrator
*
*/
public class KrpanoEntity {
/**
* 总标题
*/
private String title;
/**
* 场景标题
*/
private String[] sceneTitle;
/**
* krpano exe执行文件地址
*/
private String krpanoPath;
/**
* 源文件地址
*/
private String sourcePath;
/**
* 目标文件夹
*/
private String targetPath;
/**
* 载入动画地址
*/
private String loadingPath;
/**
* 音乐文件地址
*/
private String musicPath;
/**
* 底部logo文件地址
*/
private String logoPath;
public String getKrpanoPath() {
return krpanoPath;
}
public String getSourcePath() {
return sourcePath;
}
public String getTargetPath() {
return targetPath;
}
public String getLoadingPath() {
return loadingPath;
}
public String getMusicPath() {
return musicPath;
}
public String getLogoPath() {
return logoPath;
}
public void setKrpanoPath(String krpanoPath) {
this.krpanoPath = krpanoPath;
}
public void setSourcePath(String sourcePath) {
this.sourcePath = sourcePath;
}
public void setTargetPath(String targetPath) {
this.targetPath = targetPath;
}
public void setLoadingPath(String loadingPath) {
this.loadingPath = loadingPath;
}
public void setMusicPath(String musicPath) {
this.musicPath = musicPath;
}
public void setLogoPath(String logoPath) {
this.logoPath = logoPath;
}
public String getTitle() {
return title;
}
public String[] getSceneTitle() {
return sceneTitle;
}
public void setTitle(String title) {
this.title = title;
}
public void setSceneTitle(String[] sceneTitle) {
this.sceneTitle = sceneTitle;
}
}
krpano功能化的方法类:
package krpano;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
/**
* krpano功能化的方法
* @author Administrator
*
*/
public class UnitedMethod {
public boolean copyFile(String oldPath, String newPath) throws IOException {
File oldFile = new File(oldPath);
if (!oldFile.exists())
return false;
File newFile = new File(newPath);
// 判断是否有这个文件有不管没有创建
if (!newFile.exists()) {
newFile.mkdirs();
}
fileCopy(oldFile.getPath(),
newPath + File.separator + oldFile.getName());// 继续调用复制方法
// 递归的地方,自己调用自己的方法,就可以复制文件夹的文件夹了
return true;
}
/**
* 复制文件或者文件夹下的所有内容
* @param oldPath
* @param newPath
* @throws IOException
*/
public boolean copyFolder(String oldPath, String newPath) throws IOException {
File oldFile = new File(oldPath);
if (!oldFile.exists())
return false;
File newFile = new File(newPath);
// 判断是否有这个文件有不管没有创建
if (!newFile.exists()) {
newFile.mkdirs();
}
// 遍历文件及文件夹
for (File file : oldFile.listFiles()) {
if (file.isFile()) {
// 文件
fileCopy(file.getPath(), newPath + File.separator + file.getName()); // 调用文件拷贝的方法
} else if (file.isDirectory()) {
// 文件夹
copyFolder(file.getPath(), newPath + File.separator + file.getName());// 继续调用复制方法
// 递归的地方,自己调用自己的方法,就可以复制文件夹的文件夹了
}
}
return true;
}
private void fileCopy(String src,String des) throws IOException{
//io流固定格式
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(des));
int i = -1;//记录获取长度
byte[] bt = new byte[2014];//缓冲区
while ((i = bis.read(bt))!=-1) {
bos.write(bt, 0, i);
}
bis.close();
bos.close();
//关闭流
}
/**
* 删除文件或者文件夹下的所有内容
* @param src
*/
public void delFile(String src){
File file = new File(src);
if(!file.exists()) return;
delDir(file);
}
/**
* 递归删除文件
* @param f
*/
private static void delDir(File f) {
// 判断是否是一个目录, 不是的话跳过, 直接删除; 如果是一个目录, 先将其内容清空.
if(f.isDirectory()) {
// 获取子文件/目录
File[] subFiles = f.listFiles();
// 遍历该目录
for (File subFile : subFiles) {
// 递归调用删除该文件: 如果这是一个空目录或文件, 一次递归就可删除. 如果这是一个非空目录, 多次
// 递归清空其内容后再删除
delDir(subFile);
}
}
// 删除空目录或文件
f.delete();
}
public Document readeXMLDemo(String sourcePath) throws DocumentException{
File file = new File(sourcePath);
if(!file.exists()) return null;
// 创建saxReader对象
SAXReader reader = new SAXReader();
// 通过read方法读取一个文件 转换成Document对象
Document document = reader.read(file);
return document;
//遍历所有的元素节点
// listNodes(node);
}
/**
* 修改vr标题
* @param fileFolder
* @param title
* @throws Exception
*/
public void updateVtourTitle(String fileFolder,String title) throws Exception{
if(null == title || StringUtils.isEmpty(title)) return;
String tour_filePath = fileFolder + File.separator + KrpanoContants.TOUR_NAME;
Document tour_doc = this.readeXMLDemo(tour_filePath);
if(null == tour_doc) return;
Element tour_root = tour_doc.getRootElement();
Attribute title_attr = tour_root.attribute("title");
title_attr.setValue(title);
this.writer(tour_doc, tour_filePath);
}
/**
* 修改每个场景的标题值
* @param fileFolder
* @param sceneTitles
* @throws Exception
*/
public void updateVtourSceneTitle(String fileFolder,String[] sceneTitles) throws Exception{
if(null == sceneTitles || sceneTitles.length == 0) return;
String tour_filePath = fileFolder + File.separator + KrpanoContants.TOUR_NAME;
Document tour_doc = this.readeXMLDemo(tour_filePath);
if(null == tour_doc) return;
Element tour_root = tour_doc.getRootElement();
List<Element> sceneTitle_list = tour_root.elements("scene");
for(int i =0,size = sceneTitle_list.size();i < size;i++){
sceneTitle_list.get(i).attribute("title").setValue(sceneTitles[i]);
}
this.writer(tour_doc, tour_filePath);
}
/**
* 修改vtour.xml中的vtourskin路径地址
* @param fileFolder
* @throws Exception
*/
public void updateVtourSkinAddr(String fileFolder) throws Exception{
String tour_filePath = fileFolder + File.separator + KrpanoContants.TOUR_NAME;
Document tour_doc = this.readeXMLDemo(tour_filePath);
if(null == tour_doc) return;
Element tour_root = tour_doc.getRootElement();
Element vtourskin_element = tour_root.element("include");
Attribute attr = vtourskin_element.attribute("url");
attr.setValue("../" + attr.getValue());
this.writer(tour_doc, tour_filePath);
}
/**
* 增加vr背景音乐和控制按钮
* @param fileFolder
* @param musicPath
* @throws Exception
*/
public void addMusicElement(String fileFolder,String musicPath) throws Exception {
//tour.xml文件解析的根节点
String tour_filePath = fileFolder + File.separator + KrpanoContants.TOUR_NAME;
Document tour_doc = this.readeXMLDemo(tour_filePath);
if(null == tour_doc) return;
File music_file = new File(musicPath);
if(!music_file.exists()) return ;
Element tour_root = tour_doc.getRootElement();
Element plugin_element = tour_root.addElement("plugin");
plugin_element.addAttribute("name", "soundinterface");
plugin_element.addAttribute("url.flash", "%HTMLPATH%/plugins/soundinterface.swf");
plugin_element.addAttribute("url.html5", "%HTMLPATH%/plugins/soundinterface.js");
plugin_element.addAttribute("rootpath", "");
plugin_element.addAttribute("preload", "true");
plugin_element.addAttribute("keep", "true");
//拷贝声音文件到目录下
this.copyFile(musicPath, fileFolder);
Element action_element = tour_root.addElement("action");
action_element.addAttribute("name", "bgsnd_action");
action_element.addAttribute("autorun", "onstart");
action_element.addText("playsound(bgsnd, '%SWFPATH%/"+ music_file.getName()+"', 0);");
this.writer(tour_doc, tour_filePath);
//skin/vtourskin.xml文件解析的根节点,增加声音控制按钮
// String vtourskin_filePath =fileFolder + File.separator + VTOURSKIN_NAME;
// Document vtourskin_doc = this.readeXMLDemo(vtourskin_filePath);
// if(null == vtourskin_doc) return;
// Element vtourskin_root = vtourskin_doc.getRootElement();
// //查找bar_button节点
// List<Element> list_1 = vtourskin_root.elements();
// Element skin_layer_element = null;
// for(Element ee : list_1){
// if(null != ee.attributeValue("name") && "skin_layer".equals(ee.attributeValue("name").toString())){
// skin_layer_element = ee;
// break;
// }
// }
// List<Element> list_2 = skin_layer_element.elements();
// Element control_bar_element = null;
// for(Element ee : list_2){
// if(null != ee.attributeValue("name") && "skin_control_bar".equals(ee.attributeValue("name").toString())){
// control_bar_element = ee;
// break;
// }
// }
//
// Element bar_button_element = control_bar_element.addElement("layer");
// bar_button_element.addAttribute("name", "skin_btn_sound");
// bar_button_element.addAttribute("style", "skin_base|skin_glow");
// bar_button_element.addAttribute("crop", "64|704|64|64");
// bar_button_element.addAttribute("align", "right");
// bar_button_element.addAttribute("x", "130");
// bar_button_element.addAttribute("y", "0");
// bar_button_element.addAttribute("scale", "0.5");
// bar_button_element.addAttribute("onclick", "pausesoundtoggle(bgsnd);switch(crop,64|704|40|64,64|704|64|64);switch(alpha,1,0.25);switch(ox,0,-12)");
// this.writer(vtourskin_doc, vtourskin_filePath);
}
/**
* 新增底部logo图片
* @param fileFolder
* @param logoFloder 存放logo和配置文件的文件夹目录
* @throws Exception
*/
public void addNadirLogo(String fileFolder,String logoFloder) throws Exception{
File file = new File(fileFolder);
if(!file.exists()) return ;
File logoFile = new File(fileFolder + File.separator + KrpanoContants.LOGO_NAME);
if(!logoFile.exists()) logoFile.mkdir();
//拷贝logo文件到vr目录下
this.copyFolder(logoFloder, logoFile.getPath());
//tour.xml文件解析的根节点
String tour_filePath = fileFolder + File.separator + KrpanoContants.TOUR_NAME;
Document tour_doc = this.readeXMLDemo(tour_filePath);
if(null == tour_doc) return;
Element tour_root = tour_doc.getRootElement();
Element plugin_element = tour_root.addElement("include");
plugin_element.addAttribute("url", "logo/nadir-logo.xml");
this.writer(tour_doc, tour_filePath);
}
/**
* 增加进度条动画
* @param fileFolder
* @param loadingFloder
* @throws Exception
*/
public void addLoadingAnimation(String fileFolder,String loadingFloder) throws Exception{
File file = new File(fileFolder);
if(!file.exists()) return ;
File logoFile = new File(fileFolder + File.separator + KrpanoContants.LOADING_NAME);
if(!logoFile.exists()) logoFile.mkdir();
//拷贝logo文件到vr目录下
this.copyFolder(loadingFloder, logoFile.getPath());
//tour.xml文件解析的根节点
String tour_filePath = fileFolder + File.separator + KrpanoContants.TOUR_NAME;
Document tour_doc = this.readeXMLDemo(tour_filePath);
if(null == tour_doc) return;
Element tour_root = tour_doc.getRootElement();
Element animation_element = tour_root.addElement("include");
animation_element.addAttribute("url", "loading/loadinganimation.xml");
Element text_element = tour_root.addElement("include");
text_element.addAttribute("url", "loading/loadingpercenttext.xml");
Element bar_element = tour_root.addElement("include");
bar_element.addAttribute("url", "loading/loadingbar.xml");
this.writer(tour_doc, tour_filePath);
}
/**
* 把document对象写入新的文件
*
* @param document
* @throws Exception
*/
public void writer(Document document,String filePath) throws Exception {
// 紧凑的格式
// OutputFormat format = OutputFormat.createCompactFormat();
// 排版缩进的格式
OutputFormat format = OutputFormat.createPrettyPrint();
// 设置编码
format.setEncoding("UTF-8");
// 创建XMLWriter对象,指定了写出文件及编码格式
// XMLWriter writer = new XMLWriter(new FileWriter(new
// File("src//a.xml")),format);
XMLWriter writer = new XMLWriter(new OutputStreamWriter(
new FileOutputStream(new File(filePath)), "UTF-8"), format);
// 写入
writer.write(document);
// 立即写入
writer.flush();
// 关闭操作
writer.close();
}
public static void main(String[] args) throws Exception{
UnitedMethod um = new UnitedMethod();
String filePath = "E:\\works\\krpano\\test\\target";
//um.addMusicElement(filePath, "E:\\one.mp3");
//um.addNadirLogo(filePath, "E:\\works\\krpano\\krpano_test\\vtour\\logo");
//um.addLoadingAnimation(filePath, "E:\\works\\krpano\\krpano_test\\vtour\\loading");
//um.updateVtourSkinAddr(filePath);
um.updateVtourSceneTitle(filePath, new String[]{"千年香榧-1","千年香榧-2"});
}
}
krpano调用类:
/**
*
*/
package krpano;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
/**
* krpano执行文件
* @author Administrator
*
*/
public class ExecuteMethod {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ExecuteMethod em = new ExecuteMethod();
KrpanoEntity krEntity = new KrpanoEntity();
krEntity.setSourcePath("E:\\works\\krpano\\test");
krEntity.setTargetPath("E:\\works\\krpano\\test\\target");
krEntity.setKrpanoPath("E:\\works\\krpano\\krpano-1.19-pr14-win");
krEntity.setMusicPath("E:\\works\\krpano\\public\\one.mp3");
krEntity.setLoadingPath("E:\\works\\krpano\\public\\loading");
krEntity.setLogoPath("E:\\works\\krpano\\public\\logo");
krEntity.setTitle("一亩云-千年香榧");
krEntity.setSceneTitle(new String[]{"千年香榧-1","千年香榧-2"});
em.executeKrpanoExe(krEntity);
}
/**
* 执行krpano应用程序进行输出,输出地址为:sourcePath\\vtour
* @param krpanoPath
* @param sourcePath
* @throws InterruptedException
*/
private void executeKrpanoExe(KrpanoEntity krEntity) throws Exception{
File sourceFile = new File(krEntity.getSourcePath());
if(!sourceFile.exists()) return ;
String ex = "krpanotools32.exe makepano -config=\\templates\\vtour-vr.config "
+ krEntity.getSourcePath() + File.separator + "*.jpg";
Runtime runtime = Runtime.getRuntime();
Process p = null;
try {
p = runtime.exec("cmd /c start " + krEntity.getKrpanoPath() + File.separator + ex);
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}finally{
InputStream is1 = p.getInputStream();
new Thread() {
public void run() {
BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
try {
String line1 = null;
while ((line1 = br1.readLine()) != null) {
if (line1 != null) {
System.out.println("=AA==========line1======"
+ line1);
}
}
is1.close();
ExecuteMethod em = new ExecuteMethod();
UnitedMethod um = new UnitedMethod();
em.copyVtourFiles(krEntity.getSourcePath(),krEntity.getTargetPath());
//递归删除临时文件夹
um.delFile(krEntity.getSourcePath() + File.separator + KrpanoContants.VTOUR_NAME);
try {
um.updateVtourTitle(krEntity.getTargetPath(), krEntity.getTitle());
um.updateVtourSceneTitle(krEntity.getTargetPath(), krEntity.getSceneTitle());
um.updateVtourSkinAddr(krEntity.getTargetPath());
um.addLoadingAnimation(krEntity.getTargetPath(), krEntity.getLoadingPath());
um.addMusicElement(krEntity.getTargetPath(), krEntity.getMusicPath());
um.addNadirLogo(krEntity.getTargetPath(), krEntity.getLogoPath());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
p.destroy();
}
}
/**
* 需要拷贝3个文件,如:tour.js,tour.swf,tour.xml,还有panos文件夹下的所有文件内容
* @param sourcePath
* @param targetPath
*/
private void copyVtourFiles(String sourcePath,String targetPath){
UnitedMethod um = new UnitedMethod();
File targetFile = new File(targetPath);
if(!targetFile.exists()) targetFile.mkdir();
File sourceFile = new File(sourcePath);
if(!sourceFile.exists()) return ;
try {
um.copyFile(sourcePath + File.separator + KrpanoContants.TOUR_XML_NAME, targetPath);
um.copyFile(sourcePath + File.separator + KrpanoContants.TOUR_JS_NAME, targetPath);
um.copyFile(sourcePath + File.separator + KrpanoContants.TOUR_SWF_NAME, targetPath);
um.copyFolder(sourcePath + File.separator + KrpanoContants.PANOS_FOLDER_NANME, targetPath + File.separator + "panos");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
krpano常量类:
/**
*
*/
package krpano;
import java.io.File;
/**
* 存放krpano功能化的自定义常量
* @author Administrator
*
*/
public class KrpanoContants {
public static final String VTOUR_NAME ="vtour";
public static final String TOUR_XML_NAME = VTOUR_NAME + File.separator + "tour.xml";
public static final String TOUR_JS_NAME = VTOUR_NAME + File.separator + "tour.js";
public static final String TOUR_SWF_NAME = VTOUR_NAME + File.separator + "tour.swf";
public static final String PANOS_FOLDER_NANME = VTOUR_NAME + File.separator + "panos";
public static final String TOUR_NAME = "tour.xml";
public static final String VTOURSKIN_NAME = "skin\\vtourskin.xml";
public static final String LOGO_NAME = "logo";
public static final String LOADING_NAME = "loading";
}
下面是具体的源码下载地址:
http://download.csdn.net/download/masti/10263263