ogre在Linux下如何创建窗口,一步步搭建Ogre - 菜鸟学Ogre教程_Linux编程_Linux公社-Linux系统门户网站...

news/2024/5/20 22:12:46/文章来源:https://blog.csdn.net/weixin_34917128/article/details/116947636

这次的教程是完全脱离了wiki的框架,白手起家搭建Ogre。

Ogre的最基本的生命周期如下:

1.创建根对象;

2.定义Ogre将要用到的资源;

3.选择并设置渲染引擎(指的是DirectX,OpenGL等等);

4.创建渲染窗口;

5.初始化资源;

6.创建场景;

7.设置第三方库和插件;

8.创建帧监听;

9.开始渲染循环。

第一步:创建Root对象

Root对象是Ogre库的核心,它必须被最先创建。

在头文件中加入:

#include

private:

Ogre::Root* mRoot;

Ogre::String mPluginsCfg;

cpp文件如下:

BasicTutorial6::BasicTutorial6(void)

: mRoot(0),

mPluginsCfg(Ogre::StringUtil::BLANK)

{

}

//-------------------------------------------------------------------------------------

BasicTutorial6::~BasicTutorial6(void)

{

delete mRoot;

}

bool BasicTutorial6::go(void)

{

#ifdef _DEBUG

mPluginsCfg = "plugins_d.cfg";

#else

mPluginsCfg = "plugins.cfg";

#endif

// construct Ogre::Root

mRoot = new Ogre::Root(mPluginsCfg);

return true;

}

构造函数中,冒号后面是默认形参列表。Root对象真正的初始化是在go()中进行的。条件编译的作用是在编译debug版本和release版本时调用不同的资源。

第二步:加载资源

在头文件的私有成员中中加入

Ogre::String mResourcesCfg;

在构造函数的默认形参列表中加入代码,

BasicTutorial6::BasicTutorial6(void)

: mRoot(0),

mResourcesCfg(Ogre::StringUtil::BLANK),

mPluginsCfg(Ogre::StringUtil::BLANK)

添加头文件:

#include

然后修改go():

bool BasicTutorial6::go(void)

{

#ifdef _DEBUG

mResourcesCfg = "resources_d.cfg";

mPluginsCfg = "plugins_d.cfg";

#else

mResourcesCfg = "resources.cfg";

mPluginsCfg = "plugins.cfg";

#endif

// set up resources

// Load resource paths from config file

Ogre::ConfigFile cf;

cf.load(mResourcesCfg);

// Go through all sections & settings in the file

Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

Ogre::String secName, typeName, archName;

while (seci.hasMoreElements())

{

secName = seci.peekNextKey();

Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();

Ogre::ConfigFile::SettingsMultiMap::iterator i;

for (i = settings->begin(); i != settings->end(); ++i)

{

typeName = i->first;

archName = i->second;

Ogre::ResourceGroupManager::getSingleton().addResourceLocation(

archName, typeName, secName);

}

}

在提起资源的时候,资源文件是分成几个部分的,包括Essential Popular,General,还有两种资源类型,包括FileSystem和Zip文件。while循环就是用于循环读取资源的。

代码写到这里,你可以尝试编译运行,然后查看相应的*.log文件。

第三步:创建渲染系统

渲染系统要么是DirectX,要么是OpenGL,demo里有一个参数对话框,在设置运行参数的时候很有用,我们把它添加上去。

在头文件中添加:

Ogre::RenderWindow* mWindow;

在go()中添加:

// configure

// Show the configuration dialog and initialise the system

if(!(mRoot->restoreConfig() || mRoot->showConfigDialog()))

{

return false;

}

在if中我们先尝试恢复之前的设置,这样如果之前设置过,在运行就不用设置了,否则看能否从参数对话框中获取参数。

运用下面的语句能够同样到达设置参数的效果,

// Do not add this to the application

RenderSystem *rs = mRoot->getRenderSystemByName("Direct3D9 Rendering Subsystem");

// or use "OpenGL Rendering Subsystem"

mRoot->setRenderSystem(rs);

rs->setConfigOption("Full Screen", "No");

rs->setConfigOption("Video Mode", "800 x 600 @ 32-bit colour");

第四步:创建一个渲染窗口

这个非常简单,在go()中加入下面的这条语句:

mWindow = mRoot->initialise(true, "Render Window");

这里还不能运行窗口。

第五步:初始化资源

在初始化资源之前,还需要做一件事,就是设置贴图坐标,在go()中加入代码:

// Set default mipmap level (note: some APIs ignore this)

Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(5);

// initialise all resource groups

Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

注意go()中代码添加的顺序,一定是一步步来的。

每做完一步都试着编译运行一下,然后看一下运行日志。

第六步:创建一个场景

这里需要创建三个东西:SceneManager,Camera,Viewport.

首先加入头文件:

#include

#include

#include

#include

然后在头文件中加入:

Ogre::SceneManager* mSceneMgr;

Ogre::Camera* mCamera;

在go()中初始化:

// Create the SceneManager, in this case a generic one

mSceneMgr = mRoot->createSceneManager("DefaultSceneManager");

// Create the camera

mCamera = mSceneMgr->createCamera("PlayerCam");

// Position it at 80 in Z direction

mCamera->setPosition(Ogre::Vector3(0,0,80));

// Look back along -Z

mCamera->lookAt(Ogre::Vector3(0,0,-300));

mCamera->setNearClipDistance(5);

// Create one viewport, entire window

Ogre::Viewport* vp = mWindow->addViewport(mCamera);

vp->setBackgroundColour(Ogre::ColourValue(0,0,0));

// Alter the camera aspect ratio to match the viewport

mCamera->setAspectRatio(

Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));

第七步:添加一些东西

在go()中添加:

Ogre::Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");

Ogre::SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();

headNode->attachObject(ogreHead);

// Set ambient light

mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

// Create a light

Ogre::Light* l = mSceneMgr->createLight("MainLight");

l->setPosition(20,80,50);

在头文件中包含文件:

#include

第八步:渲染一帧来看看

在头文件中添加:

#include

在go()中添加:

while(true)

{

// Pump window messages for nice behaviour

Ogre::WindowEventUtilities::messagePump();

if(mWindow->isClosed())

{

return false;

}

// Render a frame

if(!mRoot->renderOneFrame()) return false;

}

编译运行,成功的话就能看到一个怪兽的头了!

但是我们还没有写任何鼠标和键盘的监听,所以关掉程序的方法只有alter+f4或是X窗口。

第九步:设置OIS

OIS是Ogre的最好输入工具。

添加头文件:

#include

#include

#include

#include

在类中加入私有成员:

// OIS Input devices

OIS::InputManager* mInputManager;

OIS::Mouse*    mMouse;

OIS::Keyboard* mKeyboard;

然后在编译的时候要加入相应的库

Include Directory$(OGRE_HOME)/include/OIS

Input LibraryOIS_d.lib/OIS.lib

在go()中将下面的代码加载渲染循环之前:

Ogre::LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");

OIS::ParamList pl;

size_t windowHnd = 0;

std::ostringstream windowHndStr;

mWindow->getCustomAttribute("WINDOW", &windowHnd);

windowHndStr << windowHnd;

pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

mInputManager = OIS::InputManager::createInputSystem( pl );

mKeyboard = static_cast<:keyboard>(mInputManager->createInputObject( OIS::OISKeyboard, false ));

mMouse = static_cast<:mouse>(mInputManager->createInputObject( OIS::OISMouse, false ));

接下来加入退出窗口,改变窗口大小的方法。

首先将类改为公有继承WindowEventLisrtener.

class BasicTutorial6: public Ogre::WindowEventListener

顺便复习一下C++中的继承。

公有继承时,积累中各成员属性保持不变,基类的private成员被隐藏,派生类的成员只能访问基类中的public/protected成员,派生类的对象只能访问基类中的public成员。

私有继承时基类中各成员属性均变为private,,基类的private成员被隐藏,派生类的成员只能访问基类中的public/protected成员,派生类的对象不能访问基类中的任何成员。

保护继承时基类中各成员属性均变为protected,,基类的private成员被隐藏,派生类的成员只能访问基类中的public/protected成员,派生类的对象不能访问基类中的任何成员。

接下来在头文件中加入下面两个方法:

// Ogre::WindowEventListener

virtual void windowResized(Ogre::RenderWindow* rw);

virtual void windowClosed(Ogre::RenderWindow* rw);

在cpp中写函数实现:

//Adjust mouse clipping area

void BasicTutorial6::windowResized(Ogre::RenderWindow* rw)

{

unsigned int width, height, depth;

int left, top;

rw->getMetrics(width, height, depth, left, top);

const OIS::MouseState &ms = mMouse->getMouseState();

ms.width = width;

ms.height = height;

}

//Unattach OIS before window shutdown (very important under Linux)

void BasicTutorial6::windowClosed(Ogre::RenderWindow* rw)

{

//Only close for window that created OIS (the main window in these demos)

if( rw == mWindow )

{

if( mInputManager )

{

mInputManager->destroyInputObject( mMouse );

mInputManager->destroyInputObject( mKeyboard );

OIS::InputManager::destroyInputSystem(mInputManager);

mInputManager = 0;

}

}

}

在go()中加入代码:

//Set initial mouse clipping size

windowResized(mWindow);

//Register as a Window listener

Ogre::WindowEventUtilities::addWindowEventListener(mWindow, this);

在析构函数中加入:

//Remove ourself as a Window listener

Ogre::WindowEventUtilities::removeWindowEventListener(mWindow, this);

windowClosed(mWindow);

delete mRoot;

第十步:设置帧监听

首先将共有继承FrameListener:

class BasicTutorial6 : public Ogre::WindowEventListener, public Ogre::FrameListener

头文件中加入下面的方法声明:

// Ogre::FrameListener

virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);

具体实现如下:

bool BasicTutorial6::frameRenderingQueued(const Ogre::FrameEvent& evt)

{

if(mWindow->isClosed())

return false;

//Need to capture/update each device

mKeyboard->capture();

mMouse->capture();

if(mKeyboard->isKeyDown(OIS::KC_ESCAPE))

return false;

return true;

}

第十一步:注册帧监听

在go()中加入:

mRoot->addFrameListener(this);

mRoot->startRendering();

第十一步:编译运行:

我的CMakeList.txt文件如下:

#/*

#-----------------------------------------------------------------------------

#Filename:    CMakeLists.txt

#-----------------------------------------------------------------------------

#

#This source file is part of the

#   ___                 __    __ _ _    _

#  /___\__ _ _ __ ___  / / /\ \ (_) | _(_)

# //  // _` | '__/ _ \ \ \/  \/ / | |/ / |

#/ \_// (_| | | |  __/  \  /\  /| |  

#\___/ \__, |_|  \___|   \/  \/ |_|_|\_\_|

#      |___/

#      Tutorial Framework

#      http://www.ogre3d.org/tikiwiki/

#-----------------------------------------------------------------------------

#*/

cmake_minimum_required(VERSION 2.6)

project(OgreApp)

set(CMAKE_MODULE_PATH "/usr/local/lib/OGRE/cmake/;${CMAKE_MODULE_PATH}")

set(OGRE_SAMPLES_INCLUDEPATH "/home/tao/workspace/ogre_src_v1-8-0/Samples/Common/include/")

set(OGRE_INCLUDEPATH "/usr/local/include/OGRE")

set(CMAKE_C_FLAGS "-g")

if (CMAKE_BUILD_TYPE STREQUAL "")

# CMake defaults to leaving CMAKE_BUILD_TYPE empty. This screws up

# differentiation between debug and release builds.

set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None (CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE)

endif ()

set(CMAKE_DEBUG_POSTFIX "_d")

set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/dist")

find_package(OGRE REQUIRED)

#if(NOT "${OGRE_VERSION_NAME}" STREQUAL "Cthugha")

#  message(SEND_ERROR "You need Ogre 1.7 Cthugha to build this.")

#endif()

find_package(OIS REQUIRED)

if(NOT OIS_FOUND)

message(SEND_ERROR "Failed to find OIS.")

endif()

# Find Boost

if (NOT OGRE_BUILD_PLATFORM_IPHONE)

if (WIN32 OR APPLE)

set(Boost_USE_STATIC_LIBS TRUE)

else ()

# Statically linking boost to a dynamic Ogre build doesn't work on Linux 64bit

set(Boost_USE_STATIC_LIBS ${OGRE_STATIC})

endif ()

if (MINGW)

# this is probably a bug in CMake: the boost find module tries to look for

# boost libraries with name libboost_*, but CMake already prefixes library

# search names with "lib". This is the workaround.

set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "")

endif ()

set(Boost_ADDITIONAL_VERSIONS "1.44" "1.44.0" "1.42" "1.42.0" "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37" )

# Components that need linking (NB does not include header-only components like bind)

set(OGRE_BOOST_COMPONENTS thread date_time)

find_package(Boost COMPONENTS ${OGRE_BOOST_COMPONENTS} QUIET)

if (NOT Boost_FOUND)

# Try again with the other type of libs

set(Boost_USE_STATIC_LIBS NOT ${Boost_USE_STATIC_LIBS})

find_package(Boost COMPONENTS ${OGRE_BOOST_COMPONENTS} QUIET)

endif()

find_package(Boost QUIET)

# Set up referencing of Boost

include_directories(${Boost_INCLUDE_DIR})

add_definitions(-DBOOST_ALL_NO_LIB)

set(OGRE_LIBRARIES ${OGRE_LIBRARIES} ${Boost_LIBRARIES})

endif()

set(HDRS

./TutorialApplication.h

)

set(SRCS

./TutorialApplication.cpp

)

include_directories( ${OIS_INCLUDE_DIRS}

${OGRE_INCLUDE_DIRS}

${OGRE_SAMPLES_INCLUDEPATH}

${OGRE_INCLUDEPATH}

)

add_executable(OgreApp WIN32 ${HDRS} ${SRCS})

set_target_properties(OgreApp PROPERTIES DEBUG_POSTFIX _d)

target_link_libraries(OgreApp ${OGRE_LIBRARIES} ${OIS_LIBRARIES} ${OGRE_Terrain_LIBRARIES})

file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dist/bin)

file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/dist/media)

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/dist/bin)

install(TARGETS OgreApp

RUNTIME DESTINATION bin

CONFIGURATIONS All)

install(DIRECTORY ${CMAKE_SOURCE_DIR}/dist/media

DESTINATION ./

CONFIGURATIONS Release RelWithDebInfo Debug

)

install(FILES ${CMAKE_SOURCE_DIR}/dist/bin/plugins.cfg

${CMAKE_SOURCE_DIR}/dist/bin/resources.cfg

DESTINATION bin

CONFIGURATIONS Release RelWithDebInfo Debug

)

编译如果出现段错误之类的可以用gdb来调试,找到出现错误的代码,然后修改。

最终效果:

df44c2a1f994eef9f575d0f875f2cac4.png第十二步:打完收工0b1331709591d260c1c78e86d0c51c18.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_823098.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

源代码泄露获取某电子商务网站服务器权限

源代码泄露获取某电子商务网站服务器权限 simeon 渗透本次目标事发偶然&#xff0c;通过shadon对“phpMyAdmin”关键字进行检索时&#xff0c;加入“index Of”关键字后&#xff0c;会出现所有存在列目录漏洞的网站&#xff0c;该网站为电子商务网站&#xff0c;网站保留有数万…

利用Mysql root帐号获取某Linux操作系统网站webshell

利用Mysql root帐号获取某Linux操作系统网站webshell simeon 获取Webshell&#xff0c;网上有很多文章&#xff0c;本文是phpMyAdmin漏洞利用与防范专题中的一个研究课题&#xff0c;其主要环境是在有Mysql数据库root帐号密码权限的基础下&#xff0c;如何通过技术手段获取Linu…

对某网站被挂黑广告源头分析

1.1对某网站被挂黑广告源头分析 任何的攻击都会留下痕迹,通过日志分析来发现漏洞,发现入侵的途径和路径,为漏洞分析和攻击溯源提供支持。在本案例中通过对日志文件的分析,成功获取后台地址,获取进入者的IP地址等信息。 1.1.1事件介绍 1.公司主站及其它站点被挂广告 某日晚…

对某入侵网站的一次快速处理

1.1对某入侵网站的一次快速处理 simeon 1.1.1入侵情况分析 凌晨1点,接到朋友的求助,网站被黑了,访问网站首页会自动定向到一个赌博网站,这个时间点都是该进入梦乡的时间,可是国家正在开会,这个时间点的事情都比较敏感,没有办法,直接开干吧。 1.查看首页代码 通过查看首…

4.公司网站被入侵——弱口令账号惹的祸

4.1公司网站被入侵——弱口令账号惹的祸 一些公司公司其内部网络跟web服务器直接相连,虽然在出口做了安全限制,仅仅允许80端口对外进行连接,但如果提供web服务的站点存在安全隐患,通过渗透,获取webshell,通过webshell提权,通过代理程序直接穿透内网,进而控制整个域控和…

网站服务器出现的链接错误,网站页面出现死链是哪些原因导致的?

网站死链大家都知道对于网站有较大的影响&#xff0c;一个站点存在着过多的死链是可能会导致网站降权&#xff0c;严重还会被K站&#xff0c;所以网站必须减少死链接情况出现。死链接是开始有效网站&#xff0c;过一段时间成为无效网站。到底哪些情况&#xff0c;导致死链接的出…

腾讯云服务器文件怎么恢复吗,实战腾讯云镜像备份恢复云服务器实例提取网站数据文件...

本来这个问题是准备今天操作的&#xff0c;但是担心今天要陪伴孩子出去玩&#xff0c;所以就在昨天晚上给客户搞定。这个网友的问题是服务器到期&#xff0c;然后找腾讯云客服备份镜像&#xff0c;但是他服务器已经到期删除&#xff0c;但是镜像好在还在的&#xff0c;所以我准…

python网站服务器面板,宝塔面板Python项目管理器部署flask

使用宝塔面板中的python项目管理器部署flask项目。最近想写一个工具箱的网站&#xff0c;之前使用的是PHP语言开发的&#xff0c;有一些功能是通过PHP无法实现的。在我的博客的第二个版本中就把开放平台功能模块关闭啦&#xff01;目前在使用flask框架在构建新的应用&#xff0…

PHP网站总是打印错误信息,PHP错误报告和错误信息设置详解_php

在php网站开发中&#xff0c;错误(Bugs)调试和解决是必不可少的部分&#xff0c;在网站调试阶段&#xff0c;错误信息能给我们很大帮助&#xff0c;当网站上线之后&#xff0c;我们是否也应该将原始的错误信息展示在用户面前呢&#xff1f;答案是否定的&#xff0c;为了提高用户…

Tomcat的下载及网站介绍初步测试

Tomcat下载地址 下载地址&#xff1a;https://tomcat.apache.org/ 下载完成后&#xff0c;解压压缩包&#xff1a; 文件夹信息 在bin目录下&#xff0c;startup.bat是运行tomcat、shutdown.bat是停止运行tomcat 测试tomcat 开启tomcat 双击startup.bat 会弹出&#xff1a…

怎么判断按摩店有服务_广州SEO外包服务推广哪里可以找到?怎么判断好不好?...

说到竞争压力大&#xff0c;网络营销一点都不会比线下轻&#xff0c;因为足不出户的消费习惯已成为了广大用户的习惯了。所以&#xff0c;随着移动互联网的迅速发展&#xff0c;企业需要使用有效的方法把自身的产品推广出去。而SEO推广是一种很不错的方法&#xff0c;因为它是一…

大规模网站架构的缓存机制和几何分形学

缓存机制在我们的实际研发工作中&#xff0c;被极其广泛地应用&#xff0c;通过这些缓存机制来提升系统交互的效率。简单的总结来说&#xff0c;就是在两个环节或者系统之间&#xff0c;会引入一个cache/buffer做为提升整体效率的角色。 而 有趣的是&#xff0c;这种缓存机制令…

分分钟在自己电脑上建一个视频网站,收费电影随便看,还没广告!

声明iker干货 本文仅作为技术文章&#xff0c;不鼓励不诱导用户观看盗版视频。第一步分分钟在自己电脑上建一个视频网站&#xff0c;收费电影随便看&#xff0c;还没广告&#xff01;第二步安装好软件后先切换环境&#xff0c;推荐使用 PHP 7.0.12 Apache&#xff0c;&#xf…

你需要但是找不到的网站,其实不太想分享,有你想要想收藏的

今天iker分享给大家几个平时十分需要的网站&#xff0c;如果对你有帮助麻烦点赞关注一下哦&#xff0c;谢谢了。1、Goimg.io这是一个图片压缩在线网站&#xff0c;当你在上传图片时可能会遇到图片文件太大的问题&#xff0c;这时这个网站就派上用场了。网站链接https://goimg.i…

6个在线制作海报的网站,一分钟搞定一张海报!

Fotojethttps://www.fotojet.com/cn/features/poster/FotoJet提供大量的富有设计感的海报模板&#xff0c;从音乐主题到时尚主题应有尽有。你可以选择喜欢的模板&#xff0c;再结合自己的想法&#xff0c;DIY完成你的海报。即使没有任何设计经验&#xff0c;你也可以设计出高水…

百度二级网页打不开_百度快照合理利用与网站网页制作的技巧

合理利用百度引擎进行网站宣传&#xff0c;这是网络时代企业网站发展的必要形式。目前百度搜索的排名&#xff0c;是宣传的核心与重点。而网站的整体布局&#xff0c;要追求有更好的宣传目的&#xff0c;就成为有效利用搜索引擎&#xff0c;提高网站流量的方法。当然让客户可能…

手机网站开发(WAP网站)第一篇

现在的互联网&#xff0c;可以说是名副其实的移动互联网。大家不仅可以利用无线网卡随处随地上网&#xff0c;还可以直接利用手机浏览网页、下载文件&#xff0c;而且现在的无线运营商也正在大力发展无线网络、扩展手机上网带宽。 于是&#xff0c;手机网页的制作需求也变多…

建材安装php源码,PHP响应式瓷砖大理石建材企业网站整站源码(自适应手机移动端) dedecms内核...

【温馨提示】源码包解压密码&#xff1a;www.youhutong.com资源描述PHP响应式瓷砖大理石建材企业网站整站源码(自适应手机移动端) dedecms内核源码介绍&#xff1a;采用织梦最新内核开发的模板&#xff0c;该模板企业通用、瓷砖、大理石、建材类企业都可使用。响应式自适应各种…

seo如何优化文章-知识交流_【SEO优化】网站SEO该如何优化

点击蓝字关注我哦当今的网络时代&#xff0c;酒香不怕巷子深的经营理念早已过时&#xff0c;SEO比任何时候都更重要。鉴于SEO对企业来说如此重要&#xff0c;那么具体该如何优化网站SEO呢&#xff1f;本文分别从关键词选择&#xff0c;原创文章&#xff0c;内页优化&#xff0c…

英文商城网站前端代码_门户网站关键词优化外包前端网站页面优化公司企业网站优化排名织梦网站优化霸屏商城网站推广优化现在网站seo推广优化google...

门户网站关键词优化外包前端网站页面优化公司企业网站优化排名织梦网站优化霸屏商城网站推广优化现在网站seo推广优化google门户网站关键词优化外包前端网站页面优化公司企业网站优化排名织梦网站优化霸屏商城网站推广优化现在网站seo推广优化google门户网站关键词优化外包前端…